diff --git a/README.md b/README.md index 8ca1f59..961fd9e 100644 --- a/README.md +++ b/README.md @@ -8,19 +8,23 @@ signals, and for using `class`es as Solid.js components. # Table of Contents - [At a glance](#at-a-glance) -- [Install](#install) - [`npm install classy-solid`](#npm-install-classy-solid) +- [Install](#install) + - [`npm install classy-solid`](#npm-install-classy-solid) - [Vite Setup](#vite-setup) - [Babel Setup](#babel-setup) - [API and Usage](#api-and-usage) - [Decorator APIs](#decorator-apis) - - [`@untracked`](#untracked) - - [`@reactive` (deprecated)](#reactive-deprecated) - [`@signal`](#signal) - [`@memo`](#memo) + - [`@effect`](#effect) + - [`@untracked`](#untracked) - [`@component`](#component) - [JavaScript with build tools](#javascript-with-build-tools) - [JavaScript without build tools](#javascript-without-build-tools) - - [TypeScript](#typescript) + - [Accessing the class instance with `ref`:](#accessing-the-class-instance-with-ref) + - [TypeScript definition](#typescript-definition) + - [Type-safe refs in TypeScript:](#type-safe-refs-in-typescript) + - [`@reactive` (deprecated)](#reactive-deprecated) - [Non-decorator APIs](#non-decorator-apis) - [`component()`](#component-1) - [`createSignalObject()`](#createsignalobject) @@ -171,79 +175,31 @@ Note, these docs assume you have basic knowledge of [Solid.js](https://solidjs.c ## Decorator APIs -### `@untracked` - -A class decorator that makes a class's constructor untracked, preventing signal -reads during construction from being tracked by outer effects. - -**When to use:** Only needed if your constructor reads signal/memo values. -Without `@untracked`, such a constructor will track dependencies when it is -instantiated in an effect and will cause an infinite loop. - -Example: - -```js -import {untracked, signal} from 'classy-solid' -import {createEffect} from 'solid-js' - -@untracked -class Example { - @signal count = 0 - - constructor() { - // Reading this.count here won't be tracked by outer effects - this.count = this.count + 1 - } -} - -createEffect(() => { - // This effect won't track count reads in the constructor, - // preventing infinite loops. It only runs once. - const example = new Example() - - createEffect(() => { - // This inner effect DOES track count changes - console.log(example.count) - }) -}) -``` - -Without decorators: - -```js -import {untracked} from 'classy-solid' - -const Example = untracked( - class { - count = 0 - - constructor() { - this.count = this.count + 1 - } - }, -) - -// ... same usage as above ... -``` - -### `@reactive` (deprecated) - -**⚠️ DEPRECATED:** Use [`@untracked`](#untracked) instead. This decorator is no -longer needed for making `@signal` or `@memo` work. It now serves only as an -alias to `@untracked` for backward compatibility. - ### `@signal` -Decorate a property of a class with `@signal` to make it reactive (backed by a -Solid signal). +Decorate a property (field, getter/setter, or auto accessor) of a class with +`@signal` to make it reactive (i.e. backed by a Solid signal). ```js import {signal} from 'classy-solid' import {createEffect} from 'solid-js' export class Car { + // field @signal engineOn = false - @signal sound = 'vroom' + + // auto accessor + @signal accessor sound = 'vroom' + + #speed = 0 + + // getter/setter + @signal get speed() { + return this.#speed + } + @signal set speed(value) { + this.#speed = value + } } ``` @@ -254,7 +210,8 @@ const car = new Car() createEffect(() => { // This re-runs any time car.engineOn or car.sound change. - if (car.engineOn) console.log(car.sound) + if (car.engineOn) console.log(car.sound, 'at speed', car.speed) + else console.log('engine off') }) // ... @@ -262,35 +219,38 @@ createEffect(() => { ### `@memo` -Create a memoized derived value (readonly or writable) whose computation is -cached and only re-runs when one of its reactive dependencies (f.e. `@signal` / -`signalify` properties) changes AND the computed value actually changes. This -prevents unnecessary effect executions when dependencies change but the derived -result stays the same. +Use this on a class property (getter/setter, auto accessor, or method) to create +a memoized derived value (readonly or writable) whose computation is cached and +only triggers effects when one of its reactive dependencies (f.e. `@signal` / `signalify` +properties) changes AND the computed value actually changes. This prevents +unnecessary effect executions when dependencies change but the derived result +stays the same. + +Always prefer memos over effects for derived values, even if the value always +changes, as it is more semantic to express derived values as memos. -Memos properties internally use Solid's `createMemo`/`createWritableMemo`. +Memo properties internally use Solid's `createMemo`/`createWritableMemo` +depending on the member form and function arity. Supported member forms: -| Form | Example | Call Style | Writable? | Rule | -| --------------------------- | ------------------------------------------------------- | ------------------------- | --------- | -------------------------------------- | -| Field (arrow fn) | `@memo sum = () => this.a + this.b` | `ex.sum()` | No | Function arity 0 => readonly | -| Field (arrow fn with param) | `@memo sum = (_v?: number) => this.a + this.b` | `ex.sum()` / `ex.sum(20)` | Yes | Arity > 0 => writable | -| Getter | `@memo get sum() { return this.a + this.b }` | `ex.sum` | No | Getter w/o matching setter => readonly | -| Getter + Setter | `@memo get sum() { ... }` & `@memo set sum(v) {}` | `ex.sum` / `ex.sum = 20` | Yes | Getter + (empty) setter => writable | -| Accessor (auto) readonly | `@memo accessor sum = () => this.a + this.b` | `ex.sum()` | No | Arrow fn arity 0 => readonly | -| Accessor (auto) writable | `@memo accessor sum = (_v?: number) => this.a + this.b` | `ex.sum()` / `ex.sum(20)` | Yes | Arrow fn arity > 0 => writable | -| Method readonly | `@memo sum() { return this.a + this.b }` | `ex.sum()` | No | Method arity 0 => readonly | -| Method writable | `@memo sum(_v?: number) { return this.a + this.b }` | `ex.sum()` / `ex.sum(20)` | Yes | Method arity > 0 => writable | +| Form | Example | Call Style | Writable? | Rule | +| ------------------------ | ------------------------------------------------------- | ------------------------- | --------- | -------------------------------------- | +| Getter | `@memo get sum() { return this.a + this.b }` | `ex.sum` | No | Getter w/o matching setter => readonly | +| Getter + Setter | `@memo get sum() { ... }` & `@memo set sum(v) {}` | `ex.sum` / `ex.sum = 20` | Yes | Getter + (empty) setter => writable | +| Accessor (auto) readonly | `@memo accessor sum = () => this.a + this.b` | `ex.sum()` | No | Arrow fn arity 0 => readonly | +| Accessor (auto) writable | `@memo accessor sum = (_v?: number) => this.a + this.b` | `ex.sum()` / `ex.sum(20)` | Yes | Arrow fn arity > 0 => writable | +| Method readonly | `@memo sum() { return this.a + this.b }` | `ex.sum()` | No | Method arity 0 => readonly | +| Method writable | `@memo sum(_v?: number) { return this.a + this.b }` | `ex.sum()` / `ex.sum(20)` | Yes | Method arity > 0 => writable | Writable memos: Setting a writable memo (e.g. `ex.sum(20)` or `ex.sum = 20`) overrides the current derived value. Subsequent dependency changes resume normal recomputation. Readonly memos throw if you attempt to set them. -Arity rules: A function (field, accessor, or method) with length 0 becomes a -readonly memo. A function with length > 0 becomes writable. For getter/setter -pairs, the presence of an empty setter marks the memo writable. Note, do not -provide a non-empty setter, it is ignored. +Arity rules: A function value (for auto accessors or methods) with length 0 +becomes a readonly memo. A function value with length > 0 becomes writable. For +getter/setter pairs, the presence of an empty setter marks the memo writable. +Note, do not provide a non-empty setter, it is ignored. Example: @@ -345,13 +305,254 @@ const c = new Counter() Gotchas: -- Methods and fields that become memo functions need to be invoked (`ex.sum()`), whereas getters are accessed as values (`ex.sum`). +- Methods and fields that become memo functions need to be invoked (`ex.sum()`), + whereas getters are accessed via property access (`ex.sum`). - Readonly memos should not be assigned; doing so throws. +### `@effect` + +Decorate a method or auto accessor (with a function value) with `@effect` to +automatically create a Solid.js effect for it. The effect runs during +instantiation and re-runs whenever any reactive dependencies (signals or memos) +accessed within it change. + +**Purpose:** Simplifies reactive side effects by eliminating the need to +manually call `createEffect()` in constructors or methods. Pair with `@signal` +and `@memo` to create reactive classes with minimal boilerplate. + +**Use cases:** + +- Logging or debugging reactive state changes. +- Side effects like network requests, DOM manipulation, or event subscriptions. + +**Cleanup:** Effects are tied to the class instance. Use: + +- `stopEffects(instance)` - standalone function to dispose all effects on an instance. +- `instance.stopEffects()` - when extending `Effectful(BaseClass)` or `Effects`. + +If the instance is created within an existing Solid owner (root, component, +effect), effects are disposed automatically when that owner cleans up, and +`stopEffects()` calls are unnecessary and will be a no-op. + +**Restart effects:** If you've stopped effects on an instance and want to restart +them, use: + +- `startEffects(instance)` - standalone function to restart all effects on an instance. +- `instance.startEffects()` - when extending `Effectful(BaseClass)` or `Effects`. + +**Supported forms:** + +- **Method** (recommended): `@effect methodName() { ... }` +- **Auto accessor**: `@effect accessor name = () => { ... }` + +**Example with `stopEffects()` and `startEffects()`:** + +```ts +import {effect, signal, stopEffects, startEffects} from 'classy-solid' +import {createSignal} from 'solid-js' + +const [a, setA] = createSignal(1) + +class Counter { + @signal count = 0 + + @effect logSum() { + console.log('Sum:', a() + this.count) + } + + // Auto accessor form (less common) + @effect accessor logCount = () => { + console.log('Count:', this.count) + } +} + +const counter = new Counter() // logs "Sum: 1", "Count: 0" + +setA(5) // logs "Sum: 5" +counter.count = 10 // logs "Sum: 15", "Count: 10" + +// Later, clean up when done +stopEffects(counter) + +setA(8) // does not log anything +counter.count = 20 // does not log anything + +// Later, restart effects +startEffects(counter) // logs "Sum: 28", "Count: 20" + +setA(2) // logs "Sum: 22" +``` + +**Example with [`Effectful`](#effectful) / [`Effects`](#effects) mixin:** + +```ts +import {effect, signal, memo, Effects} from 'classy-solid' +import {createSignal} from 'solid-js' + +const [a, setA] = createSignal(1) + +// Extending from the Effectful(), or the non-mixin shortcut Effects class, give +// us the stopEffects method. In this case, use `obj.stopEffects()` instead of +// `stopEffects(obj)`. +class Counter extends Effects { + @signal count = 0 + + @memo sum() { + return a() + this.count + } + + @effect logSum() { + console.log('Sum:', this.sum) + } +} + +const counter = new Counter() // logs "Sum: 1" + +setA(5) // logs "Sum: 5" +counter.count = 10 // logs "Sum: 15" + +batch(() => { + counter.count = 7 + setA(8) +}) // Does not log anything because sum did not change. + +// Later, clean up when done +counter.stopEffects() + +setA(2) // does not log anything +counter.count = 20 // does not log anything + +// Later, restart effects +counter.startEffects() // logs "Sum: 22" + +setA(3) // logs "Sum: 23" +``` + +**Why `@effect` over manual `createEffect()`?** + +- Less boilerplate (no constructor needed). +- Declarative: effects defined inline with the properties they affect. +- Automatic lifecycle management when used with `Effectful` or `Effects`. + +Without decorators: + +```js +import {signalify, Effects} from 'classy-solid' +import {createSignal} from 'solid-js' + +const [a, setA] = createSignal(1) + +class Counter extends Effects { + count = 0 + + get sum() { + return a() + this.count + } + + logSum() { + console.log('Sum:', this.sum) + } + + constructor() { + signalify(this, 'count') + memoify(this, 'sum') + this.createEffect(this.logSum.bind(this)) + } +} + +const counter = new Counter() +counter.stopEffects() // when done +``` + +**Integrate with classess that have life cycle methods:** + +For example, with Custom Element classes: + +```js +import {effect, signal, startEffects, stopEffects} from 'classy-solid' +import {externalState} from './hypothetical-app-state.js' + +class MyElement extends HTMLElement { + @effect logCount() { + console.log('Value is now:', externalState.value) + } + + connectedCallback() { + // Start effects when element is added (or re-added) to DOM. + startEffects(this) + } + + disconnectedCallback() { + // Stop effects when element is removed from DOM (if the element will not be + // used again and is unreferenced, effects are garbage collected). + stopEffects(this) + } +} + +customElements.define('my-element', MyElement) +``` + +### `@untracked` + +A class decorator that makes a class's constructor untracked, preventing signal +reads during construction from being tracked by outer effects. + +**When to use:** Only needed if your constructor reads signal/memo values. +Without `@untracked`, such a constructor will track dependencies when it is +instantiated in an effect and will cause an infinite loop. + +Example: + +```js +import {untracked, signal} from 'classy-solid' +import {createEffect} from 'solid-js' + +@untracked +class Example { + @signal count = 0 + + constructor() { + // Reading this.count here won't be tracked by outer effects + this.count = this.count + 1 + } +} + +createEffect(() => { + // This effect won't track count reads in the constructor, + // preventing infinite loops. It only runs once. + const example = new Example() + + createEffect(() => { + // This inner effect DOES track count changes + console.log(example.count) + }) +}) +``` + +Without decorators: + +```js +import {untracked} from 'classy-solid' + +const Example = untracked( + class { + count = 0 + + constructor() { + signalify(this) + // Reading this.count here won't be tracked by outer effects + this.count = this.count + 1 + } + }, +) + +// ... same usage as above ... +``` + ### `@component` A decorator that makes a `class` usable as a component within a Solid template -(f.e. within JSX markup). +(f.e. within JSX, or an `html` template tag). A class decorated with `@component` can optionally have any three of the following methods: @@ -380,26 +581,28 @@ build setup in place (soon decorators will be native in JavaScript engines and a build step will not be necessary for decorators, but will still be necessary for JSX templates). -The [Babel](https://babeljs.io/) compiler, for example, allows use of decorators and JSX: +The [Babel](https://babeljs.io/) compiler, for example, allows use of decorators +and JSX (not this example shows a variety of way to do things, but not all are +recommended, see the idiomatic example afterwards): ```jsx -import {component, signal} from 'classy-solid' +import {component, signal, memo, effect} from 'classy-solid' import {onMount, onCleanup, createEffect} from 'solid-js' export @component class MyComp { - @signal last = 'none' + @signal name = 'Anon' @signal count = 1 - h1 + #h1 onMount() { - console.log('h1 element reference:', this.h1) + console.log('onMount method, h1 element reference:', this.#h1) this.int = setInterval(() => this.count++, 1000) - // Clean up like this, + // Clean up like this (recommended), onCleanup(() => clearInterval(this.int)) } @@ -408,6 +611,28 @@ class MyComp { clearInterval(this.int) } + // Create memo properties on the class (backed by Solid createMemo). + @memo get doubleCount() { + return this.count * 2 + } + @memo set doubleCount(value) { + // providing an empty setter makes this a writable memo (backed by createWritableMemo from solid-primitives) + } + + @effect logDoubleCount() { + console.log('Double count is now:', this.doubleCount) + + // nested effects + createEffect(() => { + /*...*/ + }) + + // cleanup + onCleanup(() => { + /*...*/ + }) + } + template(props) { // The class is in a Solid reactive context. You can also use onMount or // other Solid APIs like usual. The template() method is a Solid @@ -418,94 +643,161 @@ class MyComp { // Here we show that passed-in `props` can be used directly. All // props are automatically mapped to same-name properties on the - // class instance, which is why the passed in `last={}` prop is - // accessible as `this.last`. + // class instance, which is why the passed in `count={}` prop is + // also accessible as `this.count` above. return ( -

- Hello, my name is {props.first} {this.last}! The count is {this.count}. +

+ Hello, my name is {this.name}! The count is {props.count}. The double count is {this.doubleCount}.

) } } -render(() => , document.body) +render(() => , document.body) ``` -#### JavaScript without build tools +The above example shows a class component with multiple ways of doing things +(not recommended!). When using class components, the most idiomatic practice is +to use top level effects with `@effect`, and all state in the class with +`@signal`/`@memo` without props via `template`: -> **Note** The new decorators proposal reached stage 3, so JavaScript will have -> decorators natively soon and won't require compiler support. +```jsx +import {component, signal, memo, effect} from 'classy-solid' +import {onCleanup} from 'solid-js' + +export +@component +class MyComp { + @signal name = 'Anon' + @signal count = 1 + + #h1 + + @memo doubleCount() { + return this.count * 2 + } + + onMount() { + console.log('onMount, h1 element reference:', this.#h1) + + this.int = setInterval(() => this.count++, 1000) + onCleanup(() => clearInterval(this.int)) + } + + @effect logDoubleCount() { + console.log('Double count is now:', this.doubleCount) + + // If this effect has anything to clean up: + onCleanup(() => { + /*...*/ + }) + } + + template = () => ( +

+ Hello, my name is {this.name}! The count is {this.count}. The double count is {this.doubleCount}. +

+ ) +} + +render(() => , document.body) +``` + +#### JavaScript without build tools -For plain JS users without build setups, use `component` and `signalify` with -normal function calls, and use Solid's [`html` template +For plain JS users without build setups or that prefer not using decorators or +JSX, use `component`, `signalify`, and `memoify` via plain function calls, and +use Solid's [`html` template tag](https://github.com/solidjs/solid/tree/main/packages/solid/html) for -templating: +templating because JSX requires compilation. Here's the same example as above +but without decorators or JSX: -```jsx -import {component, signalify} from 'classy-solid' +```js +import {component, signalify, memoify} from 'classy-solid' +import {onCleanup, createEffect} from 'solid-js' import html from 'solid-js/html' const MyComp = component( - class MyComp { - last = 'none' + class { + name = 'Anon' count = 1 - h1 + #h1 + + get doubleCount() { + return this.count * 2 + } constructor() { - signalify(this, 'last', 'count') - // Or, to signalify all properties (except any with function values): - // signalify(this) + // Signalify all own properties, i.e. class fields. + signalify(this) + + // Or, to signalify specific properties only (see signalify() docs). + // signalify(this, 'name', 'count') + + // Initialize memos (explicit mode because getters are not own properties). + memoify(this, 'doubleCount') + + // This will not work, because getters are not own properties: + // memoify(this) } onMount() { - console.log('h1 element:', this.h1) + console.log('h1 element:', this.#h1) this.int = setInterval(() => this.count++, 1000) - - // Clean up like this, onCleanup(() => clearInterval(this.int)) - } - // or clean up like this. - onCleanup() { - clearInterval(this.int) + createEffect(() => this.logDoubleCount()) } - template(props) { - onMount(() => console.log('mounted')) - createEffect(() => console.log('count:', this.count)) + logDoubleCount() { + console.log('Double count is now:', this.doubleCount) - return html`

(this.h1 = el)}>Hello, my name is ${() => props.first} ${() => this.last}!

` + // If this effect has anything to clean up: + onCleanup(() => { + /*...*/ + }) } + + template = () => + html`

(this.#h1 = el)}> + Hello, my name is ${() => this.name}! The count is ${() => this.count}. The double count is ${() => + this.doubleCount}. +

` }, ) -render(() => html`<${MyComp} first="Joe" last="Pea" />`, document.body) +render(() => html`<${MyComp} name="Tito Bouzout" />`, document.body) ``` +> **Note** The new decorators proposal reached stage 3, so JavaScript will have +> decorators natively soon and won't require compiler support. JSX will still +> require compilation. + For reference, here's the same example using the `component` decorator as a -regular function, but with accessor properties that wire up Solid signals -manually, which is essentially the equivalent of what the `@signal` decorator -does under the hood for convenience: +regular function, but with properties wired up to Solid signals and memos +manually, which is the equivalent of what the `@signal` and `@memo` decorators +(or `signalify()` and `memoify()` functions) do under the hood for convenience, +but with a lot more boilerplate: ```jsx import {component} from 'classy-solid' -import {createSignal} from 'solid-js' +import {createSignal, createMemo, createEffect, onCleanup} from 'solid-js' import html from 'solid-js/html' const MyComp = component( class MyComp { - #last = createSignal('none') + #name = createSignal('Anon') - get last() { + get name() { // read from a Solid signal - const [get] = this.#last + const [get] = this.#name return get() } - set last(value) { + set name(value) { // write to a Solid signal - const [, set] = this.#last + const [, set] = this.#name set(value) } @@ -520,35 +812,82 @@ const MyComp = component( set(value) } - h1 + #h1 + + #doubleCount = createMemo(() => { + return this.count * 2 + }) + + get doubleCount() { + return this.#doubleCount() + } onMount() { - console.log('h1 element:', this.h1) + console.log('h1 element:', this.#h1) this.int = setInterval(() => this.count++, 1000) - - // Clean up like this, onCleanup(() => clearInterval(this.int)) - } - // or clean up like this. - onCleanup() { - clearInterval(this.int) - } + // log doubleCount + createEffect(() => { + console.log('Double count is now:', this.doubleCount) - template(props) { - onMount(() => console.log('mounted')) - createEffect(() => console.log('count:', this.count)) - - return html`

(this.h1 = el)}>Hello, my name is ${() => props.first} ${() => this.last}!

` + // If this effect has anything to clean up: + onCleanup(() => { + /*...*/ + }) + }) } + + template = () => + html`

(this.#h1 = el)}> + Hello, my name is ${() => this.name}! The count is ${() => this.count}. The double count is ${() => + this.doubleCount}. +

` }, ) -render(() => html`<${MyComp} first="Joe" last="Pea" />`, document.body) +render(() => html`<${MyComp} name="Batman" />`, document.body) +``` + +#### Accessing the class instance with `ref`: + +The `ref` prop provides access to the actual class instance, similar to element +refs in Solid. This is useful for calling methods, accessing properties, or +integrating with imperative APIs. + +```jsx +import {component, signal} from 'classy-solid' +import {render} from 'solid-js/web' + +@component +class Counter { + @signal count = 0 + + increment() { + this.count++ + } + + template() { + return
Count: {this.count}
+ } +} + +let counterRef! + +render(() => ( + <> + (counterRef = instance)} /> + + +), document.body) ``` -#### TypeScript +The `ref` callback is called with the class instance after it's created, after +`onMount` runs. You can use it to store a reference, call methods, or pass the +instance to other parts of your app. + +#### TypeScript definition TypeScript supports decorators out of the box with no additional setup needed. @@ -556,7 +895,7 @@ TypeScript supports decorators out of the box with no additional setup needed. > JavaScript section, and the only difference here is added type checking. ```tsx -import {component, signal, Props} from 'classy-solid' +import {component, signal, memo, Props} from 'classy-solid' @component class MyComp { @@ -565,42 +904,91 @@ class MyComp { // runtime, so here we use the `declare` to tell TS not to assume it exists // for the sake of type checking. // Do not try to use this property at runtime! - declare PropTypes: Props + declare PropTypes: Props - @signal last = 'name' - @signal first = 'no' + @signal name = 'Anon' @signal count = 123 + @memo get doubleCount() { + return this.count * 2 + } + // This property will not appear in the JSX prop types, because we did not // list it in the `PropTypes` definition. foo = 'blah' - h1: HTMLHeadingElement | undefined = undefined + #h1: HTMLHeadingElement | undefined = undefined - onMount() { - console.log('h1 element:', this.h1) - } + template = () => ( +

+ Hello, my name is {this.name}! The count is {this.count}. The double count is {this.doubleCount}. +

+ ) +} - onCleanup() { - console.log('cleaned up') +render(() => , document.body) +``` + +#### Type-safe refs in TypeScript: + +```tsx +import {component, signal, Props} from 'classy-solid' + +@component +class Counter { + declare PropTypes: Props + + @signal count = 0 + increment = () => this.count++ + template = () =>
Count: {this.count}
+} + +// Then depending on what you are doing: + +// Use a ref in a class component, with class-based signal and effect +@component +class Example1 { + @signal counterRef: Counter | null = null + + // CONTINUE: make a test to ensure effects via decorator on @component classes + // are cleaned up. + @effect log() { + console.log(this.counterRef) } - template(props: this['PropTypes']) { - // Note, unlike the JS examples, we had to define a `first` property on - // the class or else it would not have a type definition here within - // `props` or in JSX that uses the component. Plain JS has no types, so - // there is no concern with that in those cases. - return ( -

- Hello, my name is {props.first} {this.last}! The count is {this.count}. -

- ) + template() { + return (this.counterRef = c)} count={5} /> } } -render(() => , document.body) +// Use a ref in a function component +function Example2() { + let [counterRef, setCounterRef] = createSignal() + + createEffect(() => console.log(counterRef())) + + return +} + +// Use a ref in a class component, in the template method like a component +@component +class Example1 { + template() { + let [counterRef, setCounterRef] = createSignal() + + createEffect(() => console.log(counterRef())) + + return + } +} ``` +### `@reactive` (deprecated) + +**⚠️ DEPRECATED:** Use [`@untracked`](#untracked) instead. This decorator is no +longer needed for making `@signal` or `@memo` work. It now serves only as an +alias to `@untracked` for backward compatibility. + ## Non-decorator APIs ### `component()` @@ -1089,8 +1477,9 @@ class CounterDisplay extends Effectful(HTMLElement) { } disconnectedCallback() { - // Stop the effects - this.stopEffects() + // Stop the effects, and clear them as new ones will be created on next + // connect. + this.clearEffects() } } @@ -1108,9 +1497,8 @@ counterEl.remove() // effects stop ### `Effects` -A pre-instantiated version of `Effectful(Object)`, providing the same -`createEffect()` and `stopEffects()` methods without requiring a mixin call, for -convenience. +A pre-instantiated version of `Effectful(Object)`, providing the same API +without requiring a mixin call, for convenience. **Purpose:** Use `Effects` when: diff --git a/dist/_state.d.ts b/dist/_state.d.ts index f1b12d2..9b19bfc 100644 --- a/dist/_state.d.ts +++ b/dist/_state.d.ts @@ -1,53 +1,25 @@ -import type { PropKey, SignalMetadata, SignalOrMemoType } from './decorators/types.js'; +import type { AnyObject, MemberStat, MetadataMembers, PropKey, ClassySolidMetadata, SignalOrMemoType } from './decorators/types.js'; +import { Effects } from './mixins/Effectful.js'; export declare const isSignalGetter: WeakSet; export declare const isMemoGetter: WeakSet; -export declare function getSignalsAndMemos(metadata: SignalMetadata): [key: PropKey, stat: { - type: SignalOrMemoType; - applied: WeakMap; -}][]; +export declare function getMembers(metadata: ClassySolidMetadata): MetadataMembers; +export declare function getMemberStat(name: PropKey, type: SignalOrMemoType, members: MetadataMembers): MemberStat; +export declare function signalifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat): void; +export declare function memoifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat): void; +/** @private internal state */ +export declare const effects__: WeakMap; +export declare function effectifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat): void; /** - * Sort certain members tracked in metadata in the order of - * - * 1. signal fields - * 2. memo fields - * 3. memo auto-accessors - * 4. memo accessors - * 5. memo methods - * - * so that any members that are normally initialized *after* - * getters/setters/methods (fields and accessors, such as signal fields, and - * memo fields and auto-accessors) will be initialized before - * getters/setters/methods (memo accessors and methods). - * - * This ensures proper initialization order which we cannot currently achieve - * with the default ordering of EcmaScript decorators alone. - */ -export declare function sortSignalsMemosInMetadata(metadata: SignalMetadata): void; -export declare function getMemberStat(name: PropKey, type: SignalOrMemoType, signalsAndMemos: any[]): any; -export declare function memoifyIfNeeded(obj: object, name: PropKey, stat: any, isAutoAccessor?: boolean): void; -/** - * If any signal-fields, memo-fields, or memo-auto-accessors are defined on the - * class (thus sorted before the given memo field), skip memoifying now (true - * return). We'll memoify later after signal fields are initialized. - */ -export declare function isPriorSignalOrMemoDefined(obj: object, name: PropKey, signalsAndMemos: any[]): boolean; -/** - * This finalizes memo initialization for memo accessors and methods that - * were waiting for all signal fields, memo fields, and memo auto-accessors - * to be initialized first. - * - * Basically we ensure that memo initialization happens in this order: - * 1. signal fields - * 2. memo fields - * 3. memo auto-accessors - * 4. memo accessors - * 5. memo methods + * This finalizes memo initialization for the members tracked, in our custom + * ordering. * * This is important because memos may depend on signals or other memos, and we - * cannot rely on EcmaScript decorator application order alone, since accessor - * and method before field decorators no matter the order in source code. + * cannot rely on EcmaScript decorator order, or extra initializer order alone, + * because accessor and method decorators/initializers run before field + * decorators no matter the order in source code (give or take some details + * regarding auto accessor ordering). * * See: https://github.com/tc39/proposal-decorators/issues/566 */ -export declare function finalizeMemos(obj: object, stat: any, signalsAndMemos: any[]): void; +export declare function finalizeMembersIfLast(obj: AnyObject, members: MetadataMembers): void; //# sourceMappingURL=_state.d.ts.map \ No newline at end of file diff --git a/dist/_state.d.ts.map b/dist/_state.d.ts.map index a895834..1b71904 100644 --- a/dist/_state.d.ts.map +++ b/dist/_state.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"_state.d.ts","sourceRoot":"","sources":["../src/_state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,OAAO,EAAE,cAAc,EAAE,gBAAgB,EAAC,MAAM,uBAAuB,CAAA;AAGpF,eAAO,MAAM,cAAc,mBAA0B,CAAA;AACrD,eAAO,MAAM,YAAY,mBAA0B,CAAA;AAMnD,wBAAgB,kBAAkB,CAAC,QAAQ,EAAE,cAAc;;;KAG1D;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,0BAA0B,CAAC,QAAQ,EAAE,cAAc,QASlE;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,eAAe,EAAE,GAAG,EAAE,OAI1F;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,EAAE,cAAc,UAAQ,QAI5F;AAED;;;;GAIG;AACH,wBAAgB,0BAA0B,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,eAAe,EAAE,GAAG,EAAE,WAU5F;AAED;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,EAAE,eAAe,EAAE,GAAG,EAAE,QAgB3E"} \ No newline at end of file +{"version":3,"file":"_state.d.ts","sourceRoot":"","sources":["../src/_state.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACX,SAAS,EACT,UAAU,EACV,eAAe,EACf,OAAO,EACP,mBAAmB,EACnB,gBAAgB,EAChB,MAAM,uBAAuB,CAAA;AAI9B,OAAO,EAAC,OAAO,EAAC,MAAM,uBAAuB,CAAA;AAE7C,eAAO,MAAM,cAAc,mBAA0B,CAAA;AACrD,eAAO,MAAM,YAAY,mBAA0B,CAAA;AAEnD,wBAAgB,UAAU,CAAC,QAAQ,EAAE,mBAAmB,mBAGvD;AAED,wBAAgB,aAAa,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,gBAAgB,EAAE,OAAO,EAAE,eAAe,cAU5F;AAgDD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,QAWhF;AAED,wBAAgB,eAAe,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,QAY9E;AAED,8BAA8B;AAC9B,eAAO,MAAM,SAAS,6BAAoC,CAAA;AAE1D,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,SAAS,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,UAAU,QA+BhF;AASD;;;;;;;;;;;GAWG;AACH,wBAAgB,qBAAqB,CAAC,GAAG,EAAE,SAAS,EAAE,OAAO,EAAE,eAAe,QAkB7E"} \ No newline at end of file diff --git a/dist/_state.js b/dist/_state.js index d288331..b691d04 100644 --- a/dist/_state.js +++ b/dist/_state.js @@ -1,103 +1,144 @@ -import { memoify } from './signals/memoify.js'; +import { memoify, setMemoifyMemberStat } from './signals/memoify.js'; +import { getInheritedDescriptor } from 'lowclass/dist/getInheritedDescriptor.js'; +import { signalify } from './signals/signalify.js'; +import { Effects } from './mixins/Effectful.js'; export const isSignalGetter = new WeakSet(); export const isMemoGetter = new WeakSet(); -const isSorted = new WeakSet(); -const typeOrder = { +export function getMembers(metadata) { + if (!Object.hasOwn(metadata, 'classySolid_members')) metadata.classySolid_members = []; // we don't extend the array from parent classes + return metadata.classySolid_members; +} +export function getMemberStat(name, type, members) { + const index = members.findIndex(member => member.name === name); + const existingStat = members[index]; + const newStat = { + type, + name, + applied: new WeakMap(), + finalize: () => {} + }; + + // replace stat in the array with the latest (f.e. duplicate class members, last one wins) + if (existingStat) members[index] = newStat;else members.push(newStat); + return newStat; +} +const isSortedCustom = new WeakSet(); + +// This is the order we want for initializing supported types of members. +const customSortOrder = { 'signal-field': 0, - 'memo-field': 1, 'memo-auto-accessor': 1, - 'memo-accessor': 2, - 'memo-method': 2 + 'memo-accessor': 1, + 'memo-method': 1, + 'effect-auto-accessor': 2, + 'effect-method': 2 }; -export function getSignalsAndMemos(metadata) { - if (!Object.hasOwn(metadata, 'signalFieldsAndMemos')) metadata.signalFieldsAndMemos = []; - return metadata.signalFieldsAndMemos; -} + +// This is the EcmaScript decorator extra initializer application order we don't want, for reference. +// Anything in the same group is applied in source order within that group. +// const ecmascriptSortOrder: Record = { +// 'memo-method': 0, +// 'memo-accessor': 0, +// 'effect-method': 0, +// 'signal-field': 1, +// 'memo-auto-accessor': 1, +// 'effect-auto-accessor': 1, +// } /** - * Sort certain members tracked in metadata in the order of - * - * 1. signal fields - * 2. memo fields - * 3. memo auto-accessors - * 4. memo accessors - * 5. memo methods + * Sort signal, memo, and effect members tracked in metadata in the order of + * our custom `memberSortOrder`. * - * so that any members that are normally initialized *after* - * getters/setters/methods (fields and accessors, such as signal fields, and - * memo fields and auto-accessors) will be initialized before - * getters/setters/methods (memo accessors and methods). + * This is so that any members that are normally initialized *after* + * getters/setters/methods (such as signal fields, and memo fields and + * auto-accessors) will be initialized before getters/setters/methods (memo + * accessors and methods), otherwise they will be initialize *after* + * getters/setters/methods due to EcmaScript decorator application order rules. * - * This ensures proper initialization order which we cannot currently achieve - * with the default ordering of EcmaScript decorators alone. + * See: https://github.com/tc39/proposal-decorators/issues/566 */ -export function sortSignalsMemosInMetadata(metadata) { - if (!metadata.signalFieldsAndMemos) return; - if (isSorted.has(metadata)) return; - isSorted.add(metadata); +function sortMetadataMembersCustomOrder(members) { + // Avoid sorting multiple times (that's why this is called in class + // initializers rather than in the decorator directly). + if (isSortedCustom.has(members)) return; + isSortedCustom.add(members); // Sort so that signal fields come first, then memo fields and // auto-accessors, finally memo accessors and methods. - metadata.signalFieldsAndMemos.sort((a, b) => typeOrder[a[1].type] - typeOrder[b[1].type]); + members.sort((a, b) => customSortOrder[a.type] - customSortOrder[b.type]); } -export function getMemberStat(name, type, signalsAndMemos) { - let stat = signalsAndMemos.find(([key]) => key === name)?.[1]; - if (!stat) signalsAndMemos.push([name, stat = { - type, - applied: new WeakMap() - }]); - return stat; +export function signalifyIfNeeded(obj, name, stat) { + if (stat.applied.get(obj)) throw new Error(`@signal decorated member "${String(name)}" has already been signalified. This can happen if there are duplicated class members.`); + signalify(obj, [name, /*untrack*/() => obj[name]]); // untrack in case obj[name] is already a signal (f.e. from a Solid Proxy) + + stat.applied.set(obj, true); } -export function memoifyIfNeeded(obj, name, stat, isAutoAccessor = false) { - if (stat.applied.get(obj)) return; - isAutoAccessor ? memoify(obj, true, name) : memoify(obj, name); +export function memoifyIfNeeded(obj, name, stat) { + if (stat.applied.get(obj)) throw new Error(`@memo decorated member "${String(name)}" has already been memoified. This can happen if there are duplicated class members.`); + setMemoifyMemberStat(stat); + memoify(obj, name); stat.applied.set(obj, true); } -/** - * If any signal-fields, memo-fields, or memo-auto-accessors are defined on the - * class (thus sorted before the given memo field), skip memoifying now (true - * return). We'll memoify later after signal fields are initialized. - */ -export function isPriorSignalOrMemoDefined(obj, name, signalsAndMemos) { - for (const [key, stat] of signalsAndMemos) { - if ((stat.type === 'signal-field' || stat.type === 'memo-field' || stat.type === 'memo-auto-accessor') && !stat.applied.get(obj)) return true; - if (key === name) break; // reached our own memo field, no prior signal-fields or memo-auto-accessors found +/** @private internal state */ +export const effects__ = new WeakMap(); +export function effectifyIfNeeded(obj, name, stat) { + if (stat.applied.get(obj)) throw new Error(`@effect decorated member "${String(name)}" has already been effectified. This can happen if there are duplicated class members.`); + const decoratorValue = stat.value; + if (!decoratorValue) throw new Error('not possible'); + const descriptor = getInheritedDescriptor(obj, name); + const leafmostMemberValue = stat.type === 'effect-auto-accessor' ? descriptor.get : obj[name]; + + // Skip base class effectify if a subclass is overriding an effect. + if (leafmostMemberValue !== decoratorValue) return; + const fn = obj[name]; + if (typeof fn !== 'function') throw new Error(`@effect decorated member "${String(name)}" is not a function: ${fn}`); + let effects = effects__.get(obj); + if (!effects) { + // If the object is already an Effects instance, use it directly. + if (obj instanceof Effects) effects__.set(obj, effects = obj); + // Otherwise, create a new Effects instance to manage the effects. + else effects__.set(obj, effects = new Effects()); } - return false; + effects.createEffect(() => fn.call(obj)); + stat.applied.set(obj, true); } /** - * This finalizes memo initialization for memo accessors and methods that - * were waiting for all signal fields, memo fields, and memo auto-accessors - * to be initialized first. - * - * Basically we ensure that memo initialization happens in this order: - * 1. signal fields - * 2. memo fields - * 3. memo auto-accessors - * 4. memo accessors - * 5. memo methods + * Count number of extra initializers called for the given members array + * per instance, so we know when the last one is called, to finalize all + * members. + */ +const extraInitializersCount = new WeakMap(); + +/** + * This finalizes memo initialization for the members tracked, in our custom + * ordering. * * This is important because memos may depend on signals or other memos, and we - * cannot rely on EcmaScript decorator application order alone, since accessor - * and method before field decorators no matter the order in source code. + * cannot rely on EcmaScript decorator order, or extra initializer order alone, + * because accessor and method decorators/initializers run before field + * decorators no matter the order in source code (give or take some details + * regarding auto accessor ordering). * * See: https://github.com/tc39/proposal-decorators/issues/566 */ -export function finalizeMemos(obj, stat, signalsAndMemos) { - const last = signalsAndMemos.findLast(([_, { - type - }]) => type === 'signal-field' || type === 'memo-field' || type === 'memo-auto-accessor'); - const [, lastStat] = last; - if (stat !== lastStat) return; +export function finalizeMembersIfLast(obj, members) { + let count = extraInitializersCount.get(obj) ?? 0; + extraInitializersCount.set(obj, ++count); + + // If this is not the last extra initializer called, return. + if (count !== members.length) return; + extraInitializersCount.set(obj, 0); + + // The last member in EcmaScript decorator extra initializer application + // order has been initialized, so we can now initialize all the members we + // track in our custom order. + sortMetadataMembersCustomOrder(members); // All signal-fields, memo-fields, and memo-auto-accessors have been // initialized. Now initialize memo fields that were waiting for // those to be ready. - for (const [key, stat] of signalsAndMemos) { - if (!(stat.type === 'memo-accessor' || stat.type === 'memo-method') || stat.applied.get(obj)) continue; - memoifyIfNeeded(obj, key, stat); - } + for (const stat of members) stat.finalize?.call(obj); } //# sourceMappingURL=_state.js.map \ No newline at end of file diff --git a/dist/_state.js.map b/dist/_state.js.map index 766c847..5403d31 100644 --- a/dist/_state.js.map +++ b/dist/_state.js.map @@ -1 +1 @@ -{"version":3,"file":"_state.js","names":["memoify","isSignalGetter","WeakSet","isMemoGetter","isSorted","typeOrder","getSignalsAndMemos","metadata","Object","hasOwn","signalFieldsAndMemos","sortSignalsMemosInMetadata","has","add","sort","a","b","type","getMemberStat","name","signalsAndMemos","stat","find","key","push","applied","WeakMap","memoifyIfNeeded","obj","isAutoAccessor","get","set","isPriorSignalOrMemoDefined","finalizeMemos","last","findLast","_","lastStat"],"sources":["../src/_state.ts"],"sourcesContent":["import type {PropKey, SignalMetadata, SignalOrMemoType} from './decorators/types.js'\nimport {memoify} from './signals/memoify.js'\n\nexport const isSignalGetter = new WeakSet()\nexport const isMemoGetter = new WeakSet()\n\nconst isSorted = new WeakSet()\n\nconst typeOrder = {'signal-field': 0, 'memo-field': 1, 'memo-auto-accessor': 1, 'memo-accessor': 2, 'memo-method': 2}\n\nexport function getSignalsAndMemos(metadata: SignalMetadata) {\n\tif (!Object.hasOwn(metadata, 'signalFieldsAndMemos')) metadata.signalFieldsAndMemos = []\n\treturn metadata.signalFieldsAndMemos!\n}\n\n/**\n * Sort certain members tracked in metadata in the order of\n *\n * 1. signal fields\n * 2. memo fields\n * 3. memo auto-accessors\n * 4. memo accessors\n * 5. memo methods\n *\n * so that any members that are normally initialized *after*\n * getters/setters/methods (fields and accessors, such as signal fields, and\n * memo fields and auto-accessors) will be initialized before\n * getters/setters/methods (memo accessors and methods).\n *\n * This ensures proper initialization order which we cannot currently achieve\n * with the default ordering of EcmaScript decorators alone.\n */\nexport function sortSignalsMemosInMetadata(metadata: SignalMetadata) {\n\tif (!metadata.signalFieldsAndMemos) return\n\n\tif (isSorted.has(metadata)) return\n\tisSorted.add(metadata)\n\n\t// Sort so that signal fields come first, then memo fields and\n\t// auto-accessors, finally memo accessors and methods.\n\tmetadata.signalFieldsAndMemos.sort((a, b) => typeOrder[a[1].type] - typeOrder[b[1].type])\n}\n\nexport function getMemberStat(name: PropKey, type: SignalOrMemoType, signalsAndMemos: any[]) {\n\tlet stat = signalsAndMemos.find(([key]) => key === name)?.[1]\n\tif (!stat) signalsAndMemos.push([name, (stat = {type, applied: new WeakMap()})])\n\treturn stat\n}\n\nexport function memoifyIfNeeded(obj: object, name: PropKey, stat: any, isAutoAccessor = false) {\n\tif (stat.applied.get(obj)) return\n\tisAutoAccessor ? memoify(obj, true, name as keyof typeof obj) : memoify(obj, name as keyof typeof obj)\n\tstat.applied.set(obj, true)\n}\n\n/**\n * If any signal-fields, memo-fields, or memo-auto-accessors are defined on the\n * class (thus sorted before the given memo field), skip memoifying now (true\n * return). We'll memoify later after signal fields are initialized.\n */\nexport function isPriorSignalOrMemoDefined(obj: object, name: PropKey, signalsAndMemos: any[]) {\n\tfor (const [key, stat] of signalsAndMemos) {\n\t\tif (\n\t\t\t(stat.type === 'signal-field' || stat.type === 'memo-field' || stat.type === 'memo-auto-accessor') &&\n\t\t\t!stat.applied.get(obj)\n\t\t)\n\t\t\treturn true\n\t\tif (key === name) break // reached our own memo field, no prior signal-fields or memo-auto-accessors found\n\t}\n\treturn false\n}\n\n/**\n * This finalizes memo initialization for memo accessors and methods that\n * were waiting for all signal fields, memo fields, and memo auto-accessors\n * to be initialized first.\n *\n * Basically we ensure that memo initialization happens in this order:\n * 1. signal fields\n * 2. memo fields\n * 3. memo auto-accessors\n * 4. memo accessors\n * 5. memo methods\n *\n * This is important because memos may depend on signals or other memos, and we\n * cannot rely on EcmaScript decorator application order alone, since accessor\n * and method before field decorators no matter the order in source code.\n *\n * See: https://github.com/tc39/proposal-decorators/issues/566\n */\nexport function finalizeMemos(obj: object, stat: any, signalsAndMemos: any[]) {\n\tconst last = signalsAndMemos.findLast(\n\t\t([_, {type}]) => type === 'signal-field' || type === 'memo-field' || type === 'memo-auto-accessor',\n\t)!\n\tconst [, lastStat] = last\n\n\tif (stat !== lastStat) return\n\n\t// All signal-fields, memo-fields, and memo-auto-accessors have been\n\t// initialized. Now initialize memo fields that were waiting for\n\t// those to be ready.\n\tfor (const [key, stat] of signalsAndMemos) {\n\t\tif (!(stat.type === 'memo-accessor' || stat.type === 'memo-method') || stat.applied.get(obj)) continue\n\n\t\tmemoifyIfNeeded(obj, key, stat)\n\t}\n}\n"],"mappings":"AACA,SAAQA,OAAO,QAAO,sBAAsB;AAE5C,OAAO,MAAMC,cAAc,GAAG,IAAIC,OAAO,CAAW,CAAC;AACrD,OAAO,MAAMC,YAAY,GAAG,IAAID,OAAO,CAAW,CAAC;AAEnD,MAAME,QAAQ,GAAG,IAAIF,OAAO,CAAiB,CAAC;AAE9C,MAAMG,SAAS,GAAG;EAAC,cAAc,EAAE,CAAC;EAAE,YAAY,EAAE,CAAC;EAAE,oBAAoB,EAAE,CAAC;EAAE,eAAe,EAAE,CAAC;EAAE,aAAa,EAAE;AAAC,CAAC;AAErH,OAAO,SAASC,kBAAkBA,CAACC,QAAwB,EAAE;EAC5D,IAAI,CAACC,MAAM,CAACC,MAAM,CAACF,QAAQ,EAAE,sBAAsB,CAAC,EAAEA,QAAQ,CAACG,oBAAoB,GAAG,EAAE;EACxF,OAAOH,QAAQ,CAACG,oBAAoB;AACrC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,0BAA0BA,CAACJ,QAAwB,EAAE;EACpE,IAAI,CAACA,QAAQ,CAACG,oBAAoB,EAAE;EAEpC,IAAIN,QAAQ,CAACQ,GAAG,CAACL,QAAQ,CAAC,EAAE;EAC5BH,QAAQ,CAACS,GAAG,CAACN,QAAQ,CAAC;;EAEtB;EACA;EACAA,QAAQ,CAACG,oBAAoB,CAACI,IAAI,CAAC,CAACC,CAAC,EAAEC,CAAC,KAAKX,SAAS,CAACU,CAAC,CAAC,CAAC,CAAC,CAACE,IAAI,CAAC,GAAGZ,SAAS,CAACW,CAAC,CAAC,CAAC,CAAC,CAACC,IAAI,CAAC,CAAC;AAC1F;AAEA,OAAO,SAASC,aAAaA,CAACC,IAAa,EAAEF,IAAsB,EAAEG,eAAsB,EAAE;EAC5F,IAAIC,IAAI,GAAGD,eAAe,CAACE,IAAI,CAAC,CAAC,CAACC,GAAG,CAAC,KAAKA,GAAG,KAAKJ,IAAI,CAAC,GAAG,CAAC,CAAC;EAC7D,IAAI,CAACE,IAAI,EAAED,eAAe,CAACI,IAAI,CAAC,CAACL,IAAI,EAAGE,IAAI,GAAG;IAACJ,IAAI;IAAEQ,OAAO,EAAE,IAAIC,OAAO,CAAC;EAAC,CAAC,CAAE,CAAC;EAChF,OAAOL,IAAI;AACZ;AAEA,OAAO,SAASM,eAAeA,CAACC,GAAW,EAAET,IAAa,EAAEE,IAAS,EAAEQ,cAAc,GAAG,KAAK,EAAE;EAC9F,IAAIR,IAAI,CAACI,OAAO,CAACK,GAAG,CAACF,GAAG,CAAC,EAAE;EAC3BC,cAAc,GAAG7B,OAAO,CAAC4B,GAAG,EAAE,IAAI,EAAET,IAAwB,CAAC,GAAGnB,OAAO,CAAC4B,GAAG,EAAET,IAAwB,CAAC;EACtGE,IAAI,CAACI,OAAO,CAACM,GAAG,CAACH,GAAG,EAAE,IAAI,CAAC;AAC5B;;AAEA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,0BAA0BA,CAACJ,GAAW,EAAET,IAAa,EAAEC,eAAsB,EAAE;EAC9F,KAAK,MAAM,CAACG,GAAG,EAAEF,IAAI,CAAC,IAAID,eAAe,EAAE;IAC1C,IACC,CAACC,IAAI,CAACJ,IAAI,KAAK,cAAc,IAAII,IAAI,CAACJ,IAAI,KAAK,YAAY,IAAII,IAAI,CAACJ,IAAI,KAAK,oBAAoB,KACjG,CAACI,IAAI,CAACI,OAAO,CAACK,GAAG,CAACF,GAAG,CAAC,EAEtB,OAAO,IAAI;IACZ,IAAIL,GAAG,KAAKJ,IAAI,EAAE,MAAK,CAAC;EACzB;EACA,OAAO,KAAK;AACb;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASc,aAAaA,CAACL,GAAW,EAAEP,IAAS,EAAED,eAAsB,EAAE;EAC7E,MAAMc,IAAI,GAAGd,eAAe,CAACe,QAAQ,CACpC,CAAC,CAACC,CAAC,EAAE;IAACnB;EAAI,CAAC,CAAC,KAAKA,IAAI,KAAK,cAAc,IAAIA,IAAI,KAAK,YAAY,IAAIA,IAAI,KAAK,oBAC/E,CAAE;EACF,MAAM,GAAGoB,QAAQ,CAAC,GAAGH,IAAI;EAEzB,IAAIb,IAAI,KAAKgB,QAAQ,EAAE;;EAEvB;EACA;EACA;EACA,KAAK,MAAM,CAACd,GAAG,EAAEF,IAAI,CAAC,IAAID,eAAe,EAAE;IAC1C,IAAI,EAAEC,IAAI,CAACJ,IAAI,KAAK,eAAe,IAAII,IAAI,CAACJ,IAAI,KAAK,aAAa,CAAC,IAAII,IAAI,CAACI,OAAO,CAACK,GAAG,CAACF,GAAG,CAAC,EAAE;IAE9FD,eAAe,CAACC,GAAG,EAAEL,GAAG,EAAEF,IAAI,CAAC;EAChC;AACD","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"_state.js","names":["memoify","setMemoifyMemberStat","getInheritedDescriptor","signalify","Effects","isSignalGetter","WeakSet","isMemoGetter","getMembers","metadata","Object","hasOwn","classySolid_members","getMemberStat","name","type","members","index","findIndex","member","existingStat","newStat","applied","WeakMap","finalize","push","isSortedCustom","customSortOrder","sortMetadataMembersCustomOrder","has","add","sort","a","b","signalifyIfNeeded","obj","stat","get","Error","String","set","memoifyIfNeeded","effects__","effectifyIfNeeded","decoratorValue","value","descriptor","leafmostMemberValue","fn","effects","createEffect","call","extraInitializersCount","finalizeMembersIfLast","count","length"],"sources":["../src/_state.ts"],"sourcesContent":["import type {\n\tAnyObject,\n\tMemberStat,\n\tMetadataMembers,\n\tPropKey,\n\tClassySolidMetadata,\n\tSignalOrMemoType,\n} from './decorators/types.js'\nimport {memoify, setMemoifyMemberStat} from './signals/memoify.js'\nimport {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js'\nimport {signalify} from './signals/signalify.js'\nimport {Effects} from './mixins/Effectful.js'\n\nexport const isSignalGetter = new WeakSet()\nexport const isMemoGetter = new WeakSet()\n\nexport function getMembers(metadata: ClassySolidMetadata) {\n\tif (!Object.hasOwn(metadata, 'classySolid_members')) metadata.classySolid_members = [] // we don't extend the array from parent classes\n\treturn metadata.classySolid_members!\n}\n\nexport function getMemberStat(name: PropKey, type: SignalOrMemoType, members: MetadataMembers) {\n\tconst index = members.findIndex(member => member.name === name)\n\tconst existingStat = members[index]\n\tconst newStat: MemberStat = {type, name, applied: new WeakMap(), finalize: () => {}}\n\n\t// replace stat in the array with the latest (f.e. duplicate class members, last one wins)\n\tif (existingStat) members[index] = newStat\n\telse members.push(newStat)\n\n\treturn newStat\n}\n\nconst isSortedCustom = new WeakSet()\n\n// This is the order we want for initializing supported types of members.\nconst customSortOrder: Record = {\n\t'signal-field': 0,\n\t'memo-auto-accessor': 1,\n\t'memo-accessor': 1,\n\t'memo-method': 1,\n\t'effect-auto-accessor': 2,\n\t'effect-method': 2,\n}\n\n// This is the EcmaScript decorator extra initializer application order we don't want, for reference.\n// Anything in the same group is applied in source order within that group.\n// const ecmascriptSortOrder: Record = {\n// \t'memo-method': 0,\n// \t'memo-accessor': 0,\n// \t'effect-method': 0,\n// \t'signal-field': 1,\n// \t'memo-auto-accessor': 1,\n// \t'effect-auto-accessor': 1,\n// }\n\n/**\n * Sort signal, memo, and effect members tracked in metadata in the order of\n * our custom `memberSortOrder`.\n *\n * This is so that any members that are normally initialized *after*\n * getters/setters/methods (such as signal fields, and memo fields and\n * auto-accessors) will be initialized before getters/setters/methods (memo\n * accessors and methods), otherwise they will be initialize *after*\n * getters/setters/methods due to EcmaScript decorator application order rules.\n *\n * See: https://github.com/tc39/proposal-decorators/issues/566\n */\nfunction sortMetadataMembersCustomOrder(members: MetadataMembers) {\n\t// Avoid sorting multiple times (that's why this is called in class\n\t// initializers rather than in the decorator directly).\n\tif (isSortedCustom.has(members)) return\n\tisSortedCustom.add(members)\n\n\t// Sort so that signal fields come first, then memo fields and\n\t// auto-accessors, finally memo accessors and methods.\n\tmembers.sort((a, b) => customSortOrder[a.type] - customSortOrder[b.type])\n}\n\nexport function signalifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat) {\n\tif (stat.applied.get(obj))\n\t\tthrow new Error(\n\t\t\t`@signal decorated member \"${String(\n\t\t\t\tname,\n\t\t\t)}\" has already been signalified. This can happen if there are duplicated class members.`,\n\t\t)\n\n\tsignalify(obj, [name, /*untrack*/ () => obj[name]]) // untrack in case obj[name] is already a signal (f.e. from a Solid Proxy)\n\n\tstat.applied.set(obj, true)\n}\n\nexport function memoifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat) {\n\tif (stat.applied.get(obj))\n\t\tthrow new Error(\n\t\t\t`@memo decorated member \"${String(\n\t\t\t\tname,\n\t\t\t)}\" has already been memoified. This can happen if there are duplicated class members.`,\n\t\t)\n\n\tsetMemoifyMemberStat(stat)\n\tmemoify(obj, name as keyof typeof obj)\n\n\tstat.applied.set(obj, true)\n}\n\n/** @private internal state */\nexport const effects__ = new WeakMap()\n\nexport function effectifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat) {\n\tif (stat.applied.get(obj))\n\t\tthrow new Error(\n\t\t\t`@effect decorated member \"${String(\n\t\t\t\tname,\n\t\t\t)}\" has already been effectified. This can happen if there are duplicated class members.`,\n\t\t)\n\n\tconst decoratorValue = stat.value as Function\n\tif (!decoratorValue) throw new Error('not possible')\n\n\tconst descriptor = getInheritedDescriptor(obj, name)!\n\tconst leafmostMemberValue = stat.type === 'effect-auto-accessor' ? descriptor.get : obj[name]\n\n\t// Skip base class effectify if a subclass is overriding an effect.\n\tif (leafmostMemberValue !== decoratorValue) return\n\n\tconst fn = obj[name]\n\tif (typeof fn !== 'function') throw new Error(`@effect decorated member \"${String(name)}\" is not a function: ${fn}`)\n\n\tlet effects = effects__.get(obj)\n\tif (!effects) {\n\t\t// If the object is already an Effects instance, use it directly.\n\t\tif (obj instanceof Effects) effects__.set(obj, (effects = obj))\n\t\t// Otherwise, create a new Effects instance to manage the effects.\n\t\telse effects__.set(obj, (effects = new Effects()))\n\t}\n\n\teffects.createEffect(() => fn.call(obj))\n\n\tstat.applied.set(obj, true)\n}\n\n/**\n * Count number of extra initializers called for the given members array\n * per instance, so we know when the last one is called, to finalize all\n * members.\n */\nconst extraInitializersCount = new WeakMap()\n\n/**\n * This finalizes memo initialization for the members tracked, in our custom\n * ordering.\n *\n * This is important because memos may depend on signals or other memos, and we\n * cannot rely on EcmaScript decorator order, or extra initializer order alone,\n * because accessor and method decorators/initializers run before field\n * decorators no matter the order in source code (give or take some details\n * regarding auto accessor ordering).\n *\n * See: https://github.com/tc39/proposal-decorators/issues/566\n */\nexport function finalizeMembersIfLast(obj: AnyObject, members: MetadataMembers) {\n\tlet count = extraInitializersCount.get(obj) ?? 0\n\textraInitializersCount.set(obj, ++count)\n\n\t// If this is not the last extra initializer called, return.\n\tif (count !== members.length) return\n\n\textraInitializersCount.set(obj, 0)\n\n\t// The last member in EcmaScript decorator extra initializer application\n\t// order has been initialized, so we can now initialize all the members we\n\t// track in our custom order.\n\tsortMetadataMembersCustomOrder(members)\n\n\t// All signal-fields, memo-fields, and memo-auto-accessors have been\n\t// initialized. Now initialize memo fields that were waiting for\n\t// those to be ready.\n\tfor (const stat of members) stat.finalize?.call(obj)\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/component.d.ts b/dist/decorators/component.d.ts index df7dbf0..b63c57a 100644 --- a/dist/decorators/component.d.ts +++ b/dist/decorators/component.d.ts @@ -1,5 +1,6 @@ import { Constructor } from 'lowclass/dist/Constructor.js'; import { type JSX } from 'solid-js'; +import './metadata-shim.js'; /** * A decorator for using classes as Solid components. * @@ -7,7 +8,6 @@ import { type JSX } from 'solid-js'; * * ```js * ⁣@component - * ⁣@reactive * class MyComp { * ⁣@signal last = 'none' * @@ -38,5 +38,6 @@ declare module 'solid-js' { } export type Props = Pick & { children?: JSX.Element; + ref?: (component: T) => void; }; //# sourceMappingURL=component.d.ts.map \ No newline at end of file diff --git a/dist/decorators/component.d.ts.map b/dist/decorators/component.d.ts.map index dfdd06d..da6b446 100644 --- a/dist/decorators/component.d.ts.map +++ b/dist/decorators/component.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/decorators/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAA;AACxD,OAAO,EAAmC,KAAK,GAAG,EAAqB,MAAM,UAAU,CAAA;AAWvF;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,GAAG,CAsCzF;AAED,OAAO,QAAQ,UAAU,CAAC;IACzB,UAAU,GAAG,CAAC;QAEb,UAAU,YAAY;YACrB,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,OAAO,CAAA;SACtD;QAGD,UAAU,yBAAyB;YAClC,SAAS,EAAE,EAAE,CAAA;SACb;KACD;CACD;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IACrE,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;CACtB,CAAA"} \ No newline at end of file +{"version":3,"file":"component.d.ts","sourceRoot":"","sources":["../../src/decorators/component.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAA;AACxD,OAAO,EAAmC,KAAK,GAAG,EAAqB,MAAM,UAAU,CAAA;AACvF,OAAO,oBAAoB,CAAA;AAW3B;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,WAAW,EAAE,IAAI,EAAE,CAAC,EAAE,OAAO,CAAC,EAAE,gBAAgB,GAAG,GAAG,CA4DzF;AAED,OAAO,QAAQ,UAAU,CAAC;IACzB,UAAU,GAAG,CAAC;QAEb,UAAU,YAAY;YACrB,QAAQ,CAAC,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,GAAG,CAAC,OAAO,CAAA;SACtD;QAGD,UAAU,yBAAyB;YAClC,SAAS,EAAE,EAAE,CAAA;SACb;KACD;CACD;AAED,MAAM,MAAM,KAAK,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,IAAI,IAAI,CAAC,CAAC,EAAE,CAAC,CAAC,GAAG;IACrE,QAAQ,CAAC,EAAE,GAAG,CAAC,OAAO,CAAA;IACtB,GAAG,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,CAAA;CAC5B,CAAA"} \ No newline at end of file diff --git a/dist/decorators/component.js b/dist/decorators/component.js index 4b82afe..e653849 100644 --- a/dist/decorators/component.js +++ b/dist/decorators/component.js @@ -1,5 +1,6 @@ import { Constructor } from 'lowclass/dist/Constructor.js'; import { onMount, createEffect, onCleanup, $TRACK, createMemo } from 'solid-js'; +import './metadata-shim.js'; // https://github.com/ryansolid/dom-expressions/pull/122 @@ -10,7 +11,6 @@ import { onMount, createEffect, onCleanup, $TRACK, createMemo } from 'solid-js'; * * ```js * ⁣@component - * ⁣@reactive * class MyComp { * ⁣@signal last = 'none' * @@ -31,7 +31,11 @@ import { onMount, createEffect, onCleanup, $TRACK, createMemo } from 'solid-js'; export function component(Base, context) { if (typeof Base !== 'function' || context && context.kind !== 'class') throw new Error('The @component decorator should only be used on a class.'); const Class = Constructor(Base); - return function (props) { + + // Solid only undetstands function components, so we create a wrapper + // function that instantiates the class and hooks up lifecycle methods and + // props. + function classComponentWrapper(props) { const instance = new Class(); const keys = createMemo(() => { props[$TRACK]; @@ -44,16 +48,31 @@ export function component(Base, context) { } }); createEffect(() => { - for (const prop of keys()) { - createEffect(() => { - // @ts-expect-error - instance[prop] = props[prop]; - }); - } + // @ts-expect-error index signature + for (const prop of keys()) createEffect(() => instance[prop] = props[prop]); + }); + onMount(() => { + instance.onMount?.(); + createEffect(() => { + const ref = props.ref; + ref?.(instance); + }); + onCleanup(() => instance.onCleanup?.()); }); - if (instance.onMount) onMount(() => instance.onMount?.()); - if (instance.onCleanup) onCleanup(() => instance.onCleanup?.()); return instance.template?.(props) ?? null; - }; + } + Object.defineProperties(classComponentWrapper, { + name: { + value: Class.name, + configurable: true + }, + [Symbol.hasInstance]: { + value(obj) { + return obj instanceof Class; + }, + configurable: true + } + }); + return classComponentWrapper; } //# sourceMappingURL=component.js.map \ No newline at end of file diff --git a/dist/decorators/component.js.map b/dist/decorators/component.js.map index a6f9494..81dbd49 100644 --- a/dist/decorators/component.js.map +++ b/dist/decorators/component.js.map @@ -1 +1 @@ -{"version":3,"file":"component.js","names":["Constructor","onMount","createEffect","onCleanup","$TRACK","createMemo","component","Base","context","kind","Error","Class","props","instance","keys","Object","equals","prev","next","length","i","l","prop","template"],"sources":["../../src/decorators/component.ts"],"sourcesContent":["import {Constructor} from 'lowclass/dist/Constructor.js'\nimport {onMount, createEffect, onCleanup, type JSX, $TRACK, createMemo} from 'solid-js'\n\n// https://github.com/ryansolid/dom-expressions/pull/122\n\ninterface PossibleComponent {\n\tonMount?(): void\n\tonCleanup?(): void\n\ttemplate?(props: Record): JSX.Element\n}\ninterface PossiblyReactiveConstructor {}\n\n/**\n * A decorator for using classes as Solid components.\n *\n * Example:\n *\n * ```js\n * ⁣@component\n * ⁣@reactive\n * class MyComp {\n * ⁣@signal last = 'none'\n *\n * onMount() {\n * console.log('mounted')\n * }\n *\n * template(props) {\n * // here we use `props` passed in, or the signal on `this` which is also\n * // treated as a prop\n * return

Hello, my name is {props.first} {this.last}

\n * }\n * }\n *\n * render(() => )\n * ```\n */\nexport function component(Base: T, context?: DecoratorContext): any {\n\tif (typeof Base !== 'function' || (context && context.kind !== 'class'))\n\t\tthrow new Error('The @component decorator should only be used on a class.')\n\n\tconst Class = Constructor(Base)\n\n\treturn function (props: Record): JSX.Element {\n\t\tconst instance = new Class()\n\n\t\tconst keys = createMemo(\n\t\t\t() => {\n\t\t\t\tprops[$TRACK]\n\t\t\t\treturn Object.keys(props)\n\t\t\t},\n\t\t\t[],\n\t\t\t{\n\t\t\t\tequals(prev, next) {\n\t\t\t\t\tif (prev.length !== next.length) return false\n\t\t\t\t\tfor (let i = 0, l = prev.length; i < l; i += 1) if (prev[i] !== next[i]) return false\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\n\t\tcreateEffect(() => {\n\t\t\tfor (const prop of keys()) {\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\t// @ts-expect-error\n\t\t\t\t\tinstance[prop] = props[prop]\n\t\t\t\t})\n\t\t\t}\n\t\t})\n\n\t\tif (instance.onMount) onMount(() => instance.onMount?.())\n\t\tif (instance.onCleanup) onCleanup(() => instance.onCleanup?.())\n\n\t\treturn instance.template?.(props) ?? null\n\t}\n}\n\ndeclare module 'solid-js' {\n\tnamespace JSX {\n\t\t// Tells JSX what properties class components should have.\n\t\tinterface ElementClass {\n\t\t\ttemplate?(props: Record): JSX.Element\n\t\t}\n\n\t\t// Tells JSX where to look up prop types on class components.\n\t\tinterface ElementAttributesProperty {\n\t\t\tPropTypes: {}\n\t\t}\n\t}\n}\n\nexport type Props = Pick & {\n\tchildren?: JSX.Element\n}\n"],"mappings":"AAAA,SAAQA,WAAW,QAAO,8BAA8B;AACxD,SAAQC,OAAO,EAAEC,YAAY,EAAEC,SAAS,EAAYC,MAAM,EAAEC,UAAU,QAAO,UAAU;;AAEvF;;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,SAASA,CAAwBC,IAAO,EAAEC,OAA0B,EAAO;EAC1F,IAAI,OAAOD,IAAI,KAAK,UAAU,IAAKC,OAAO,IAAIA,OAAO,CAACC,IAAI,KAAK,OAAQ,EACtE,MAAM,IAAIC,KAAK,CAAC,0DAA0D,CAAC;EAE5E,MAAMC,KAAK,GAAGX,WAAW,CAAiDO,IAAI,CAAC;EAE/E,OAAO,UAAUK,KAAuC,EAAe;IACtE,MAAMC,QAAQ,GAAG,IAAIF,KAAK,CAAC,CAAC;IAE5B,MAAMG,IAAI,GAAGT,UAAU,CACtB,MAAM;MACLO,KAAK,CAACR,MAAM,CAAC;MACb,OAAOW,MAAM,CAACD,IAAI,CAACF,KAAK,CAAC;IAC1B,CAAC,EACD,EAAE,EACF;MACCI,MAAMA,CAACC,IAAI,EAAEC,IAAI,EAAE;QAClB,IAAID,IAAI,CAACE,MAAM,KAAKD,IAAI,CAACC,MAAM,EAAE,OAAO,KAAK;QAC7C,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEC,CAAC,GAAGJ,IAAI,CAACE,MAAM,EAAEC,CAAC,GAAGC,CAAC,EAAED,CAAC,IAAI,CAAC,EAAE,IAAIH,IAAI,CAACG,CAAC,CAAC,KAAKF,IAAI,CAACE,CAAC,CAAC,EAAE,OAAO,KAAK;QACrF,OAAO,IAAI;MACZ;IACD,CACD,CAAC;IAEDlB,YAAY,CAAC,MAAM;MAClB,KAAK,MAAMoB,IAAI,IAAIR,IAAI,CAAC,CAAC,EAAE;QAC1BZ,YAAY,CAAC,MAAM;UAClB;UACAW,QAAQ,CAACS,IAAI,CAAC,GAAGV,KAAK,CAACU,IAAI,CAAC;QAC7B,CAAC,CAAC;MACH;IACD,CAAC,CAAC;IAEF,IAAIT,QAAQ,CAACZ,OAAO,EAAEA,OAAO,CAAC,MAAMY,QAAQ,CAACZ,OAAO,GAAG,CAAC,CAAC;IACzD,IAAIY,QAAQ,CAACV,SAAS,EAAEA,SAAS,CAAC,MAAMU,QAAQ,CAACV,SAAS,GAAG,CAAC,CAAC;IAE/D,OAAOU,QAAQ,CAACU,QAAQ,GAAGX,KAAK,CAAC,IAAI,IAAI;EAC1C,CAAC;AACF","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"component.js","names":["Constructor","onMount","createEffect","onCleanup","$TRACK","createMemo","component","Base","context","kind","Error","Class","classComponentWrapper","props","instance","keys","Object","equals","prev","next","length","i","l","prop","ref","template","defineProperties","name","value","configurable","Symbol","hasInstance","obj"],"sources":["../../src/decorators/component.ts"],"sourcesContent":["import {Constructor} from 'lowclass/dist/Constructor.js'\nimport {onMount, createEffect, onCleanup, type JSX, $TRACK, createMemo} from 'solid-js'\nimport './metadata-shim.js'\n\n// https://github.com/ryansolid/dom-expressions/pull/122\n\ninterface PossibleComponent {\n\tonMount?(): void\n\tonCleanup?(): void\n\ttemplate?(props: Record): JSX.Element\n}\ninterface PossiblyReactiveConstructor {}\n\n/**\n * A decorator for using classes as Solid components.\n *\n * Example:\n *\n * ```js\n * ⁣@component\n * class MyComp {\n * ⁣@signal last = 'none'\n *\n * onMount() {\n * console.log('mounted')\n * }\n *\n * template(props) {\n * // here we use `props` passed in, or the signal on `this` which is also\n * // treated as a prop\n * return

Hello, my name is {props.first} {this.last}

\n * }\n * }\n *\n * render(() => )\n * ```\n */\nexport function component(Base: T, context?: DecoratorContext): any {\n\tif (typeof Base !== 'function' || (context && context.kind !== 'class'))\n\t\tthrow new Error('The @component decorator should only be used on a class.')\n\n\tconst Class = Constructor(Base)\n\n\t// Solid only undetstands function components, so we create a wrapper\n\t// function that instantiates the class and hooks up lifecycle methods and\n\t// props.\n\tfunction classComponentWrapper(props: Record): JSX.Element {\n\t\tconst instance = new Class()\n\n\t\tconst keys = createMemo(\n\t\t\t() => {\n\t\t\t\tprops[$TRACK]\n\t\t\t\treturn Object.keys(props)\n\t\t\t},\n\t\t\t[],\n\t\t\t{\n\t\t\t\tequals(prev, next) {\n\t\t\t\t\tif (prev.length !== next.length) return false\n\t\t\t\t\tfor (let i = 0, l = prev.length; i < l; i += 1) if (prev[i] !== next[i]) return false\n\t\t\t\t\treturn true\n\t\t\t\t},\n\t\t\t},\n\t\t)\n\n\t\tcreateEffect(() => {\n\t\t\t// @ts-expect-error index signature\n\t\t\tfor (const prop of keys()) createEffect(() => (instance[prop] = props[prop]))\n\t\t})\n\n\t\tonMount(() => {\n\t\t\tinstance.onMount?.()\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tconst ref = props.ref as ((component: PossibleComponent) => void) | undefined\n\t\t\t\tref?.(instance)\n\t\t\t})\n\n\t\t\tonCleanup(() => instance.onCleanup?.())\n\t\t})\n\n\t\treturn instance.template?.(props) ?? null\n\t}\n\n\tObject.defineProperties(classComponentWrapper, {\n\t\tname: {\n\t\t\tvalue: Class.name,\n\t\t\tconfigurable: true,\n\t\t},\n\t\t[Symbol.hasInstance]: {\n\t\t\tvalue(obj: unknown) {\n\t\t\t\treturn obj instanceof Class\n\t\t\t},\n\t\t\tconfigurable: true,\n\t\t},\n\t})\n\n\treturn classComponentWrapper\n}\n\ndeclare module 'solid-js' {\n\tnamespace JSX {\n\t\t// Tells JSX what properties class components should have.\n\t\tinterface ElementClass {\n\t\t\ttemplate?(props: Record): JSX.Element\n\t\t}\n\n\t\t// Tells JSX where to look up prop types on class components.\n\t\tinterface ElementAttributesProperty {\n\t\t\tPropTypes: {}\n\t\t}\n\t}\n}\n\nexport type Props = Pick & {\n\tchildren?: JSX.Element\n\tref?: (component: T) => void\n}\n"],"mappings":"AAAA,SAAQA,WAAW,QAAO,8BAA8B;AACxD,SAAQC,OAAO,EAAEC,YAAY,EAAEC,SAAS,EAAYC,MAAM,EAAEC,UAAU,QAAO,UAAU;AACvF,OAAO,oBAAoB;;AAE3B;;AASA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,SAASA,CAAwBC,IAAO,EAAEC,OAA0B,EAAO;EAC1F,IAAI,OAAOD,IAAI,KAAK,UAAU,IAAKC,OAAO,IAAIA,OAAO,CAACC,IAAI,KAAK,OAAQ,EACtE,MAAM,IAAIC,KAAK,CAAC,0DAA0D,CAAC;EAE5E,MAAMC,KAAK,GAAGX,WAAW,CAAiDO,IAAI,CAAC;;EAE/E;EACA;EACA;EACA,SAASK,qBAAqBA,CAACC,KAAuC,EAAe;IACpF,MAAMC,QAAQ,GAAG,IAAIH,KAAK,CAAC,CAAC;IAE5B,MAAMI,IAAI,GAAGV,UAAU,CACtB,MAAM;MACLQ,KAAK,CAACT,MAAM,CAAC;MACb,OAAOY,MAAM,CAACD,IAAI,CAACF,KAAK,CAAC;IAC1B,CAAC,EACD,EAAE,EACF;MACCI,MAAMA,CAACC,IAAI,EAAEC,IAAI,EAAE;QAClB,IAAID,IAAI,CAACE,MAAM,KAAKD,IAAI,CAACC,MAAM,EAAE,OAAO,KAAK;QAC7C,KAAK,IAAIC,CAAC,GAAG,CAAC,EAAEC,CAAC,GAAGJ,IAAI,CAACE,MAAM,EAAEC,CAAC,GAAGC,CAAC,EAAED,CAAC,IAAI,CAAC,EAAE,IAAIH,IAAI,CAACG,CAAC,CAAC,KAAKF,IAAI,CAACE,CAAC,CAAC,EAAE,OAAO,KAAK;QACrF,OAAO,IAAI;MACZ;IACD,CACD,CAAC;IAEDnB,YAAY,CAAC,MAAM;MAClB;MACA,KAAK,MAAMqB,IAAI,IAAIR,IAAI,CAAC,CAAC,EAAEb,YAAY,CAAC,MAAOY,QAAQ,CAACS,IAAI,CAAC,GAAGV,KAAK,CAACU,IAAI,CAAE,CAAC;IAC9E,CAAC,CAAC;IAEFtB,OAAO,CAAC,MAAM;MACba,QAAQ,CAACb,OAAO,GAAG,CAAC;MAEpBC,YAAY,CAAC,MAAM;QAClB,MAAMsB,GAAG,GAAGX,KAAK,CAACW,GAA2D;QAC7EA,GAAG,GAAGV,QAAQ,CAAC;MAChB,CAAC,CAAC;MAEFX,SAAS,CAAC,MAAMW,QAAQ,CAACX,SAAS,GAAG,CAAC,CAAC;IACxC,CAAC,CAAC;IAEF,OAAOW,QAAQ,CAACW,QAAQ,GAAGZ,KAAK,CAAC,IAAI,IAAI;EAC1C;EAEAG,MAAM,CAACU,gBAAgB,CAACd,qBAAqB,EAAE;IAC9Ce,IAAI,EAAE;MACLC,KAAK,EAAEjB,KAAK,CAACgB,IAAI;MACjBE,YAAY,EAAE;IACf,CAAC;IACD,CAACC,MAAM,CAACC,WAAW,GAAG;MACrBH,KAAKA,CAACI,GAAY,EAAE;QACnB,OAAOA,GAAG,YAAYrB,KAAK;MAC5B,CAAC;MACDkB,YAAY,EAAE;IACf;EACD,CAAC,CAAC;EAEF,OAAOjB,qBAAqB;AAC7B","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/component.test.js b/dist/decorators/component.test.js index 78087a1..578d587 100644 --- a/dist/decorators/component.test.js +++ b/dist/decorators/component.test.js @@ -6,23 +6,31 @@ function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side import html from 'solid-js/html'; import { component } from './component.js'; import { render } from 'solid-js/web'; -import { reactive } from './reactive.js'; import { signal } from './signal.js'; import { createSignal } from 'solid-js'; import { createSignalFunction } from '../signals/createSignalFunction.js'; import { signalify } from '../signals/signalify.js'; import { createMutable } from 'solid-js/store'; +import { memo } from './memo.js'; +import { effect } from './effect.js'; describe('classy-solid', () => { describe('@component', () => { it('allows to define a class using class syntax', () => { - let _initClass; + let _initClass, _init_foo, _init_extra_foo; let onMountCalled = false; let onCleanupCalled = false; let _CoolComp; class CoolComp { static { - [_CoolComp, _initClass] = _applyDecs(this, [component], []).c; + ({ + e: [_init_foo, _init_extra_foo], + c: [_CoolComp, _initClass] + } = _applyDecs(this, [component], [[signal, 0, "foo"]])); + } + constructor() { + _init_extra_foo(this); } + foo = _init_foo(this, 0); onMount() { onMountCalled = true; } @@ -30,23 +38,31 @@ describe('classy-solid', () => { onCleanupCalled = true; } template(props) { - expect(props.foo).toBe(123); - return html`
hello classes!
`; + expect(props.foo).toBe(123); // not recommended to access props this way + + expect(this.foo).toBe(0); // initial value only + + return html`
hello classes! ${() => this.foo}
`; } static { _initClass(); } } + + // Component classes cannot be instantiated directly, they can only + // be used as Solid components in JSX or html templates. + expect(() => new _CoolComp()).toThrow(); const root = document.createElement('div'); document.body.append(root); const dispose = render(() => html`<${_CoolComp} foo=${123} />`, root); - expect(root.textContent).toBe('hello classes!'); + expect(root.textContent).toBe('hello classes! 123'); expect(onMountCalled).toBe(true); expect(onCleanupCalled).toBe(false); dispose(); root.remove(); expect(onCleanupCalled).toBe(true); - + }); + it('throws on invalid use', () => { // throws on non-class use expect(() => { let _initProto; @@ -63,44 +79,86 @@ describe('classy-solid', () => { CoolComp; }).toThrow('component decorator should only be used on a class'); }); - it('works in tandem with @reactive and @signal for reactivity', async () => { - let _initClass2, _init_foo, _init_extra_foo, _init_bar, _init_extra_bar; + it('allows getting a ref to the class instance', () => { + let _initClass2; let _CoolComp2; + class CoolComp { + static { + [_CoolComp2, _initClass2] = _applyDecs(this, [component], []).c; + } + coolness = Infinity; + template = () => html`
hello refs!
`; + static { + _initClass2(); + } + } + const root = document.createElement('div'); + document.body.append(root); + let compRef; + const dispose = render(() => html`<${_CoolComp2} ref=${comp => compRef = comp} />`, root); + expect(root.textContent).toBe('hello refs!'); + expect(compRef instanceof _CoolComp2).toBe(true); + expect(compRef.coolness).toBe(Infinity); + dispose(); + root.remove(); + }); + it('works in tandem with @signal, @memo, and @effect for reactivity', async () => { + let _initProto2, _initClass3, _init_foo2, _init_extra_foo2, _init_bar, _init_extra_bar; + let _CoolComp3; class CoolComp { static { ({ - e: [_init_foo, _init_extra_foo, _init_bar, _init_extra_bar], - c: [_CoolComp2, _initClass2] - } = _applyDecs(this, [component, reactive], [[signal, 0, "foo"], [signal, 0, "bar"]])); + e: [_init_foo2, _init_extra_foo2, _init_bar, _init_extra_bar, _initProto2], + c: [_CoolComp3, _initClass3] + } = _applyDecs(this, [component], [[signal, 0, "foo"], [signal, 0, "bar"], [memo, 3, "sum"], [effect, 2, "logSum"]])); } - constructor() { - _init_extra_bar(this); + foo = (_initProto2(this), _init_foo2(this, 0)); + bar = (_init_extra_foo2(this), _init_bar(this, 0)); + get sum() { + return this.foo + this.bar; + } + runs = (_init_extra_bar(this), 0); + result = 0; + logSum() { + this.runs++; + this.result = this.sum; } - foo = _init_foo(this, 0); - bar = (_init_extra_foo(this), _init_bar(this, 0)); template() { return html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`; } static { - _initClass2(); + _initClass3(); } } const root = document.createElement('div'); document.body.append(root); const [a, setA] = createSignal(1); const b = createSignalFunction(2); + let compRef; // FIXME Why do we need `() => b()` instead of just `b` here? Does `html` // check the `length` of the function and do something based on // that? Or does it get passed to a @signal property's setter and // receives the previous value? - const dispose = render(() => html`<${_CoolComp2} foo=${a} bar=${() => b()} />`, root); + const dispose = render(() => html` <${_CoolComp3} ref=${comp => compRef = comp} foo=${a} bar=${() => b()} /> `, root); expect(root.textContent).toBe('foo: 1, bar: 2'); + expect(compRef.result).toBe(3); + expect(compRef.runs).toBe(2); // 1 initial run with 0 and 0, 1 run from setting foo and bar props + setA(3); + expect(root.textContent).toBe('foo: 3, bar: 2'); + expect(compRef.result).toBe(5); + expect(compRef.runs).toBe(3); b(4); expect(root.textContent).toBe('foo: 3, bar: 4'); + expect(compRef.result).toBe(7); + expect(compRef.runs).toBe(4); dispose(); root.remove(); + setA(5); + b(6); + expect(compRef.result).toBe(7); // no change after dispose + expect(compRef.runs).toBe(4); // no change after dispose }); it('works without decorators', () => { const CoolComp = component(class CoolComp { @@ -131,27 +189,28 @@ describe('classy-solid', () => { root.remove(); }); - // FIXME not working, the spread doesn't seem to do anything. - xit('works with reactive spreads', async () => { - let _initClass3, _init_foo2, _init_extra_foo2, _init_bar2, _init_extra_bar2; - let _CoolComp3; + // FIXME not working, spread syntax not supported yet in solid-js/html + // TODO unit test using JSX + it.skip('works with reactive spreads', async () => { + let _initClass4, _init_foo3, _init_extra_foo3, _init_bar2, _init_extra_bar2; + let _CoolComp4; class CoolComp { static { ({ - e: [_init_foo2, _init_extra_foo2, _init_bar2, _init_extra_bar2], - c: [_CoolComp3, _initClass3] - } = _applyDecs(this, [component, reactive], [[signal, 0, "foo"], [signal, 0, "bar"]])); + e: [_init_foo3, _init_extra_foo3, _init_bar2, _init_extra_bar2], + c: [_CoolComp4, _initClass4] + } = _applyDecs(this, [component], [[signal, 0, "foo"], [signal, 0, "bar"]])); } constructor() { _init_extra_bar2(this); } - foo = _init_foo2(this, 0); - bar = (_init_extra_foo2(this), _init_bar2(this, 0)); + foo = _init_foo3(this, 0); + bar = (_init_extra_foo3(this), _init_bar2(this, 0)); template() { return html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`; } static { - _initClass3(); + _initClass4(); } } const root = document.createElement('div'); @@ -164,7 +223,7 @@ describe('classy-solid', () => { // neither of these work // const dispose = render(() => html`<${CoolComp} ...${() => o.o} />`, root) - const dispose = render(() => html`<${_CoolComp3} ...${o.o} />`, root); + const dispose = render(() => html`<${_CoolComp4} ...${o.o} />`, root); expect(root.textContent).toBe('foo: 123, bar: 0'); o.o = { bar: 456 diff --git a/dist/decorators/component.test.js.map b/dist/decorators/component.test.js.map index 5c80b99..817d8b9 100644 --- a/dist/decorators/component.test.js.map +++ b/dist/decorators/component.test.js.map @@ -1 +1 @@ -{"version":3,"file":"component.test.js","names":["html","component","render","reactive","signal","createSignal","createSignalFunction","signalify","createMutable","describe","it","_initClass","onMountCalled","onCleanupCalled","_CoolComp","CoolComp","_applyDecs","c","onMount","onCleanup","template","props","expect","foo","toBe","root","document","createElement","body","append","dispose","textContent","remove","_initProto","e","constructor","toThrow","_initClass2","_init_foo","_init_extra_foo","_init_bar","_init_extra_bar","_CoolComp2","bar","a","setA","b","xit","_initClass3","_init_foo2","_init_extra_foo2","_init_bar2","_init_extra_bar2","_CoolComp3","o"],"sources":["../../src/decorators/component.test.ts"],"sourcesContent":["import html from 'solid-js/html'\nimport {component} from './component.js'\nimport {render} from 'solid-js/web'\nimport {reactive} from './reactive.js'\nimport {signal} from './signal.js'\nimport {createSignal} from 'solid-js'\nimport {createSignalFunction} from '../signals/createSignalFunction.js'\nimport {signalify} from '../signals/signalify.js'\nimport {createMutable} from 'solid-js/store'\n\ndescribe('classy-solid', () => {\n\tdescribe('@component', () => {\n\t\tit('allows to define a class using class syntax', () => {\n\t\t\tlet onMountCalled = false\n\t\t\tlet onCleanupCalled = false\n\n\t\t\t@component\n\t\t\tclass CoolComp {\n\t\t\t\tonMount() {\n\t\t\t\t\tonMountCalled = true\n\t\t\t\t}\n\n\t\t\t\tonCleanup() {\n\t\t\t\t\tonCleanupCalled = true\n\t\t\t\t}\n\n\t\t\t\ttemplate(props: any) {\n\t\t\t\t\texpect(props.foo).toBe(123)\n\t\t\t\t\treturn html`
hello classes!
`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tconst dispose = render(() => html`<${CoolComp} foo=${123} />`, root)\n\n\t\t\texpect(root.textContent).toBe('hello classes!')\n\t\t\texpect(onMountCalled).toBe(true)\n\t\t\texpect(onCleanupCalled).toBe(false)\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\n\t\t\texpect(onCleanupCalled).toBe(true)\n\n\t\t\t// throws on non-class use\n\t\t\texpect(() => {\n\t\t\t\tclass CoolComp {\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\t@component\n\t\t\t\t\tonMount() {}\n\t\t\t\t}\n\t\t\t\tCoolComp\n\t\t\t}).toThrow('component decorator should only be used on a class')\n\t\t})\n\n\t\tit('works in tandem with @reactive and @signal for reactivity', async () => {\n\t\t\t@component\n\t\t\t@reactive\n\t\t\tclass CoolComp {\n\t\t\t\t@signal foo = 0\n\t\t\t\t@signal bar = 0\n\n\t\t\t\ttemplate() {\n\t\t\t\t\treturn html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tconst b = createSignalFunction(2)\n\n\t\t\t// FIXME Why do we need `() => b()` instead of just `b` here? Does `html`\n\t\t\t// check the `length` of the function and do something based on\n\t\t\t// that? Or does it get passed to a @signal property's setter and\n\t\t\t// receives the previous value?\n\t\t\tconst dispose = render(() => html`<${CoolComp} foo=${a} bar=${() => b()} />`, root)\n\n\t\t\texpect(root.textContent).toBe('foo: 1, bar: 2')\n\n\t\t\tsetA(3)\n\t\t\tb(4)\n\n\t\t\texpect(root.textContent).toBe('foo: 3, bar: 4')\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\t\t})\n\n\t\tit('works without decorators', () => {\n\t\t\tconst CoolComp = component(\n\t\t\t\tclass CoolComp {\n\t\t\t\t\tfoo = 0\n\t\t\t\t\tbar = 0\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsignalify(this)\n\t\t\t\t\t}\n\n\t\t\t\t\ttemplate() {\n\t\t\t\t\t\treturn html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t)\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tconst b = createSignalFunction(2)\n\n\t\t\t// FIXME Why do we need `() => b()` instead of just `b` here? Does `html`\n\t\t\t// check the `length` of the function and do something based on\n\t\t\t// that? Or does it get passed to a @signal property's setter and\n\t\t\t// receives the previous value?\n\t\t\tconst dispose = render(() => html`<${CoolComp} foo=${a} bar=${() => b()} />`, root)\n\n\t\t\texpect(root.textContent).toBe('foo: 1, bar: 2')\n\n\t\t\tsetA(3)\n\t\t\tb(4)\n\n\t\t\texpect(root.textContent).toBe('foo: 3, bar: 4')\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\t\t})\n\n\t\t// FIXME not working, the spread doesn't seem to do anything.\n\t\txit('works with reactive spreads', async () => {\n\t\t\t@component\n\t\t\t@reactive\n\t\t\tclass CoolComp {\n\t\t\t\t@signal foo = 0\n\t\t\t\t@signal bar = 0\n\n\t\t\t\ttemplate() {\n\t\t\t\t\treturn html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tlet o = createMutable({o: {foo: 123}})\n\n\t\t\t// neither of these work\n\t\t\t// const dispose = render(() => html`<${CoolComp} ...${() => o.o} />`, root)\n\t\t\tconst dispose = render(() => html`<${CoolComp} ...${o.o} />`, root)\n\n\t\t\texpect(root.textContent).toBe('foo: 123, bar: 0')\n\n\t\t\to.o = {bar: 456}\n\n\t\t\texpect(root.textContent).toBe('foo: 123, bar: 456')\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"component.test.js","names":["html","component","render","signal","createSignal","createSignalFunction","signalify","createMutable","memo","effect","describe","it","_initClass","_init_foo","_init_extra_foo","onMountCalled","onCleanupCalled","_CoolComp","CoolComp","e","c","_applyDecs","constructor","foo","onMount","onCleanup","template","props","expect","toBe","toThrow","root","document","createElement","body","append","dispose","textContent","remove","_initProto","_initClass2","_CoolComp2","coolness","Infinity","compRef","comp","_initProto2","_initClass3","_init_foo2","_init_extra_foo2","_init_bar","_init_extra_bar","_CoolComp3","bar","sum","runs","result","logSum","a","setA","b","skip","_initClass4","_init_foo3","_init_extra_foo3","_init_bar2","_init_extra_bar2","_CoolComp4","o"],"sources":["../../src/decorators/component.test.ts"],"sourcesContent":["import html from 'solid-js/html'\nimport {component, type Props} from './component.js'\nimport {render} from 'solid-js/web'\nimport {signal} from './signal.js'\nimport {createSignal} from 'solid-js'\nimport {createSignalFunction} from '../signals/createSignalFunction.js'\nimport {signalify} from '../signals/signalify.js'\nimport {createMutable} from 'solid-js/store'\nimport {memo} from './memo.js'\nimport {effect} from './effect.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('@component', () => {\n\t\tit('allows to define a class using class syntax', () => {\n\t\t\tlet onMountCalled = false\n\t\t\tlet onCleanupCalled = false\n\n\t\t\t@component\n\t\t\tclass CoolComp {\n\t\t\t\tdeclare PropTypes: Props\n\n\t\t\t\t@signal foo = 0\n\n\t\t\t\tonMount() {\n\t\t\t\t\tonMountCalled = true\n\t\t\t\t}\n\n\t\t\t\tonCleanup() {\n\t\t\t\t\tonCleanupCalled = true\n\t\t\t\t}\n\n\t\t\t\ttemplate(props: this['PropTypes']) {\n\t\t\t\t\texpect(props.foo).toBe(123) // not recommended to access props this way\n\n\t\t\t\t\texpect(this.foo).toBe(0) // initial value only\n\n\t\t\t\t\treturn html`
hello classes! ${() => this.foo}
`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Component classes cannot be instantiated directly, they can only\n\t\t\t// be used as Solid components in JSX or html templates.\n\t\t\texpect(() => new CoolComp()).toThrow()\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tconst dispose = render(() => html`<${CoolComp} foo=${123} />`, root)\n\n\t\t\texpect(root.textContent).toBe('hello classes! 123')\n\t\t\texpect(onMountCalled).toBe(true)\n\t\t\texpect(onCleanupCalled).toBe(false)\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\n\t\t\texpect(onCleanupCalled).toBe(true)\n\t\t})\n\n\t\tit('throws on invalid use', () => {\n\t\t\t// throws on non-class use\n\t\t\texpect(() => {\n\t\t\t\tclass CoolComp {\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\t@component\n\t\t\t\t\tonMount() {}\n\t\t\t\t}\n\t\t\t\tCoolComp\n\t\t\t}).toThrow('component decorator should only be used on a class')\n\t\t})\n\n\t\tit('allows getting a ref to the class instance', () => {\n\t\t\t@component\n\t\t\tclass CoolComp {\n\t\t\t\tcoolness = Infinity\n\t\t\t\ttemplate = () => html`
hello refs!
`\n\t\t\t}\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tlet compRef!: CoolComp\n\n\t\t\tconst dispose = render(() => html`<${CoolComp} ref=${(comp: CoolComp) => (compRef = comp)} />`, root)\n\n\t\t\texpect(root.textContent).toBe('hello refs!')\n\t\t\texpect(compRef instanceof CoolComp).toBe(true)\n\t\t\texpect(compRef.coolness).toBe(Infinity)\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\t\t})\n\n\t\tit('works in tandem with @signal, @memo, and @effect for reactivity', async () => {\n\t\t\t@component\n\t\t\tclass CoolComp {\n\t\t\t\tdeclare PropTypes: Props\n\n\t\t\t\t@signal foo = 0\n\t\t\t\t@signal bar = 0\n\n\t\t\t\t@memo get sum() {\n\t\t\t\t\treturn this.foo + this.bar\n\t\t\t\t}\n\n\t\t\t\truns = 0\n\t\t\t\tresult = 0\n\t\t\t\t@effect logSum() {\n\t\t\t\t\tthis.runs++\n\t\t\t\t\tthis.result = this.sum\n\t\t\t\t}\n\n\t\t\t\ttemplate() {\n\t\t\t\t\treturn html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tconst b = createSignalFunction(2)\n\n\t\t\tlet compRef!: CoolComp\n\n\t\t\t// FIXME Why do we need `() => b()` instead of just `b` here? Does `html`\n\t\t\t// check the `length` of the function and do something based on\n\t\t\t// that? Or does it get passed to a @signal property's setter and\n\t\t\t// receives the previous value?\n\t\t\tconst dispose = render(\n\t\t\t\t() => html` <${CoolComp} ref=${(comp: CoolComp) => (compRef = comp)} foo=${a} bar=${() => b()} /> `,\n\t\t\t\troot,\n\t\t\t)\n\n\t\t\texpect(root.textContent).toBe('foo: 1, bar: 2')\n\t\t\texpect(compRef.result).toBe(3)\n\t\t\texpect(compRef.runs).toBe(2) // 1 initial run with 0 and 0, 1 run from setting foo and bar props\n\n\t\t\tsetA(3)\n\t\t\texpect(root.textContent).toBe('foo: 3, bar: 2')\n\t\t\texpect(compRef.result).toBe(5)\n\t\t\texpect(compRef.runs).toBe(3)\n\n\t\t\tb(4)\n\t\t\texpect(root.textContent).toBe('foo: 3, bar: 4')\n\t\t\texpect(compRef.result).toBe(7)\n\t\t\texpect(compRef.runs).toBe(4)\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\t\t\tsetA(5)\n\t\t\tb(6)\n\t\t\texpect(compRef.result).toBe(7) // no change after dispose\n\t\t\texpect(compRef.runs).toBe(4) // no change after dispose\n\t\t})\n\n\t\tit('works without decorators', () => {\n\t\t\tconst CoolComp = component(\n\t\t\t\tclass CoolComp {\n\t\t\t\t\tfoo = 0\n\t\t\t\t\tbar = 0\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsignalify(this)\n\t\t\t\t\t}\n\n\t\t\t\t\ttemplate() {\n\t\t\t\t\t\treturn html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`\n\t\t\t\t\t}\n\t\t\t\t},\n\t\t\t)\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tconst b = createSignalFunction(2)\n\n\t\t\t// FIXME Why do we need `() => b()` instead of just `b` here? Does `html`\n\t\t\t// check the `length` of the function and do something based on\n\t\t\t// that? Or does it get passed to a @signal property's setter and\n\t\t\t// receives the previous value?\n\t\t\tconst dispose = render(() => html`<${CoolComp} foo=${a} bar=${() => b()} />`, root)\n\n\t\t\texpect(root.textContent).toBe('foo: 1, bar: 2')\n\n\t\t\tsetA(3)\n\t\t\tb(4)\n\n\t\t\texpect(root.textContent).toBe('foo: 3, bar: 4')\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\t\t})\n\n\t\t// FIXME not working, spread syntax not supported yet in solid-js/html\n\t\t// TODO unit test using JSX\n\t\tit.skip('works with reactive spreads', async () => {\n\t\t\t@component\n\t\t\tclass CoolComp {\n\t\t\t\t@signal foo = 0\n\t\t\t\t@signal bar = 0\n\n\t\t\t\ttemplate() {\n\t\t\t\t\treturn html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst root = document.createElement('div')\n\t\t\tdocument.body.append(root)\n\n\t\t\tlet o = createMutable({o: {foo: 123}})\n\n\t\t\t// neither of these work\n\t\t\t// const dispose = render(() => html`<${CoolComp} ...${() => o.o} />`, root)\n\t\t\tconst dispose = render(() => html`<${CoolComp} ...${o.o} />`, root)\n\n\t\t\texpect(root.textContent).toBe('foo: 123, bar: 0')\n\n\t\t\to.o = {bar: 456}\n\n\t\t\texpect(root.textContent).toBe('foo: 123, bar: 456')\n\n\t\t\tdispose()\n\t\t\troot.remove()\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/effect.d.ts b/dist/decorators/effect.d.ts new file mode 100644 index 0000000..974478d --- /dev/null +++ b/dist/decorators/effect.d.ts @@ -0,0 +1,93 @@ +import './metadata-shim.js'; +/** + * Decorator for making Solid.js effects out of methods or function-valued + * properties. This is more convenient than calling `this.createEffect()` in the + * constructor or a class method, reducing boilerplate. Pair this with `@signal` + * and `@memo` to make reactive classes with less code. + * + * The `@effect` decorator can be used on methods or auto accessors. Methods are + * the recommended usage. + * + * When used on auto accessors, the auto accessor value must be a function. Note + * that currently the auto accessor function value cannot be changed (if you + * change it, the new function will not be used). + * + * Example: + * + * ```ts + * import { effect, signal, stopEffects } from 'classy-solid' + * import { createSignal } from 'solid-js' + * + * const [a, setA] = createSignal(1) + * + * class Funkalicious { + * @signal b = 2 + * + * @effect logSum() { + * console.log('Sum:', a() + this.b) + * } + * + * // Not recommended, but supported (more concise for simple effects): + * @effect accessor logA = () => console.log('a:', a()) + * } + * + * const fun = new Funkalicious() // logs "Sum: 3" + * + * setA(5) // logs "Sum: 7", "a: 5" + * fun.b = 10 // logs "Sum: 15" + * + * // Later, clean up when done... + * stopEffects(fun) + * ``` + * + * When extending from Effectful() or Effects, the `stopEffects` method can + * be used instead of the standalone `stopEffects()` function: + * + * ```ts + * import { effect, signal, Effects } from 'classy-solid' + * import { createSignal } from 'solid-js' + * + * const [a, setA] = createSignal(1) + * + * class Funkalicious extends Effects { + * @signal b = 2 + * + * @effect logSum() { + * console.log('Sum:', a() + this.b) + * } + * } + * + * const fun = new Funkalicious() // logs "Sum: 3" + * + * setA(5) // logs "Sum: 7" + * fun.b = 10 // logs "Sum: 15" + * + * // Later, clean up when done... + * fun.stopEffects() + * ``` + */ +export declare function effect(value: Function | ClassAccessorDecoratorTarget void>, context: ClassMethodDecoratorContext | ClassAccessorDecoratorContext): void; +/** + * Starts all Solid.js effects created by the `@effect` decorator on the given + * object. This can be used to restart effects that were previously stopped with + * `stopEffects()`. + * + * Effects are created and started automatically, so this only needs to be + * called if you have previously stopped the effects and want to start them + * again. + */ +export declare function startEffects(obj: object): void; +/** + * Stops all Solid.js effects created by the `@effect` decorator on the given + * object. Use this once you are done with the instance and need to clean up. + * + * This does not needed to be used if the object is created in a reactive + * context (such as inside a Solid.js component, or a nested effect), as those + * effects will be cleaned up automatically when the owner context is cleaned + * up. + * + * Effects that have been stopped can later be restarted by calling + * `startEffects(obj)`. + */ +export declare function stopEffects(obj: object): void; +//# sourceMappingURL=effect.d.ts.map \ No newline at end of file diff --git a/dist/decorators/effect.d.ts.map b/dist/decorators/effect.d.ts.map new file mode 100644 index 0000000..f8c7001 --- /dev/null +++ b/dist/decorators/effect.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"effect.d.ts","sourceRoot":"","sources":["../../src/decorators/effect.ts"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAA;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkEG;AAEH,wBAAgB,MAAM,CACrB,KAAK,EAAE,QAAQ,GAAG,4BAA4B,CAAC,OAAO,EAAE,MAAM,IAAI,CAAC,EACnE,OAAO,EAAE,2BAA2B,GAAG,6BAA6B,QA0BpE;AAED;;;;;;;;GAQG;AACH,wBAAgB,YAAY,CAAC,GAAG,EAAE,MAAM,QAIvC;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,QAItC"} \ No newline at end of file diff --git a/dist/decorators/effect.js b/dist/decorators/effect.js new file mode 100644 index 0000000..e47553c --- /dev/null +++ b/dist/decorators/effect.js @@ -0,0 +1,123 @@ +import { effectifyIfNeeded, finalizeMembersIfLast, getMemberStat, getMembers, effects__ } from '../_state.js'; +import './metadata-shim.js'; + +/** + * Decorator for making Solid.js effects out of methods or function-valued + * properties. This is more convenient than calling `this.createEffect()` in the + * constructor or a class method, reducing boilerplate. Pair this with `@signal` + * and `@memo` to make reactive classes with less code. + * + * The `@effect` decorator can be used on methods or auto accessors. Methods are + * the recommended usage. + * + * When used on auto accessors, the auto accessor value must be a function. Note + * that currently the auto accessor function value cannot be changed (if you + * change it, the new function will not be used). + * + * Example: + * + * ```ts + * import { effect, signal, stopEffects } from 'classy-solid' + * import { createSignal } from 'solid-js' + * + * const [a, setA] = createSignal(1) + * + * class Funkalicious { + * @signal b = 2 + * + * @effect logSum() { + * console.log('Sum:', a() + this.b) + * } + * + * // Not recommended, but supported (more concise for simple effects): + * @effect accessor logA = () => console.log('a:', a()) + * } + * + * const fun = new Funkalicious() // logs "Sum: 3" + * + * setA(5) // logs "Sum: 7", "a: 5" + * fun.b = 10 // logs "Sum: 15" + * + * // Later, clean up when done... + * stopEffects(fun) + * ``` + * + * When extending from Effectful() or Effects, the `stopEffects` method can + * be used instead of the standalone `stopEffects()` function: + * + * ```ts + * import { effect, signal, Effects } from 'classy-solid' + * import { createSignal } from 'solid-js' + * + * const [a, setA] = createSignal(1) + * + * class Funkalicious extends Effects { + * @signal b = 2 + * + * @effect logSum() { + * console.log('Sum:', a() + this.b) + * } + * } + * + * const fun = new Funkalicious() // logs "Sum: 3" + * + * setA(5) // logs "Sum: 7" + * fun.b = 10 // logs "Sum: 15" + * + * // Later, clean up when done... + * fun.stopEffects() + * ``` + */ + +export function effect(value, context) { + if (context.static) throw new Error('@effect is not supported on static members.'); + const { + kind, + name + } = context; + const metadata = context.metadata; + const signalsAndMemos = getMembers(metadata); + if (!(kind === 'method' || kind === 'accessor')) throw new Error('@effect can only be used on methods or function-valued accessors'); + const stat = kind === 'accessor' ? getMemberStat(name, 'effect-auto-accessor', signalsAndMemos) : getMemberStat(name, 'effect-method', signalsAndMemos); + stat.finalize = function () { + effectifyIfNeeded(this, name, stat); + }; + context.addInitializer(function () { + finalizeMembersIfLast(this, signalsAndMemos); + }); + if (kind === 'method') stat.value = value;else if (kind === 'accessor') stat.value = value.get; +} + +/** + * Starts all Solid.js effects created by the `@effect` decorator on the given + * object. This can be used to restart effects that were previously stopped with + * `stopEffects()`. + * + * Effects are created and started automatically, so this only needs to be + * called if you have previously stopped the effects and want to start them + * again. + */ +export function startEffects(obj) { + let effects = effects__.get(obj); + if (!effects) return; + effects.startEffects(); +} + +/** + * Stops all Solid.js effects created by the `@effect` decorator on the given + * object. Use this once you are done with the instance and need to clean up. + * + * This does not needed to be used if the object is created in a reactive + * context (such as inside a Solid.js component, or a nested effect), as those + * effects will be cleaned up automatically when the owner context is cleaned + * up. + * + * Effects that have been stopped can later be restarted by calling + * `startEffects(obj)`. + */ +export function stopEffects(obj) { + const effects = effects__.get(obj); + if (!effects) return; + effects.stopEffects(); +} +//# sourceMappingURL=effect.js.map \ No newline at end of file diff --git a/dist/decorators/effect.js.map b/dist/decorators/effect.js.map new file mode 100644 index 0000000..9543a06 --- /dev/null +++ b/dist/decorators/effect.js.map @@ -0,0 +1 @@ +{"version":3,"file":"effect.js","names":["effectifyIfNeeded","finalizeMembersIfLast","getMemberStat","getMembers","effects__","effect","value","context","static","Error","kind","name","metadata","signalsAndMemos","stat","finalize","addInitializer","get","startEffects","obj","effects","stopEffects"],"sources":["../../src/decorators/effect.ts"],"sourcesContent":["import {effectifyIfNeeded, finalizeMembersIfLast, getMemberStat, getMembers, effects__} from '../_state.js'\nimport type {AnyObject, ClassySolidMetadata} from './types.js'\nimport './metadata-shim.js'\n\n/**\n * Decorator for making Solid.js effects out of methods or function-valued\n * properties. This is more convenient than calling `this.createEffect()` in the\n * constructor or a class method, reducing boilerplate. Pair this with `@signal`\n * and `@memo` to make reactive classes with less code.\n *\n * The `@effect` decorator can be used on methods or auto accessors. Methods are\n * the recommended usage.\n *\n * When used on auto accessors, the auto accessor value must be a function. Note\n * that currently the auto accessor function value cannot be changed (if you\n * change it, the new function will not be used).\n *\n * Example:\n *\n * ```ts\n * import { effect, signal, stopEffects } from 'classy-solid'\n * import { createSignal } from 'solid-js'\n *\n * const [a, setA] = createSignal(1)\n *\n * class Funkalicious {\n * @signal b = 2\n *\n * @effect logSum() {\n * console.log('Sum:', a() + this.b)\n * }\n *\n * // Not recommended, but supported (more concise for simple effects):\n * @effect accessor logA = () => console.log('a:', a())\n * }\n *\n * const fun = new Funkalicious() // logs \"Sum: 3\"\n *\n * setA(5) // logs \"Sum: 7\", \"a: 5\"\n * fun.b = 10 // logs \"Sum: 15\"\n *\n * // Later, clean up when done...\n * stopEffects(fun)\n * ```\n *\n * When extending from Effectful() or Effects, the `stopEffects` method can\n * be used instead of the standalone `stopEffects()` function:\n *\n * ```ts\n * import { effect, signal, Effects } from 'classy-solid'\n * import { createSignal } from 'solid-js'\n *\n * const [a, setA] = createSignal(1)\n *\n * class Funkalicious extends Effects {\n * @signal b = 2\n *\n * @effect logSum() {\n * console.log('Sum:', a() + this.b)\n * }\n * }\n *\n * const fun = new Funkalicious() // logs \"Sum: 3\"\n *\n * setA(5) // logs \"Sum: 7\"\n * fun.b = 10 // logs \"Sum: 15\"\n *\n * // Later, clean up when done...\n * fun.stopEffects()\n * ```\n */\n\nexport function effect(\n\tvalue: Function | ClassAccessorDecoratorTarget void>,\n\tcontext: ClassMethodDecoratorContext | ClassAccessorDecoratorContext,\n) {\n\tif (context.static) throw new Error('@effect is not supported on static members.')\n\n\tconst {kind, name} = context\n\tconst metadata = context.metadata as ClassySolidMetadata\n\tconst signalsAndMemos = getMembers(metadata)\n\n\tif (!(kind === 'method' || kind === 'accessor'))\n\t\tthrow new Error('@effect can only be used on methods or function-valued accessors')\n\n\tconst stat =\n\t\tkind === 'accessor'\n\t\t\t? getMemberStat(name, 'effect-auto-accessor', signalsAndMemos)\n\t\t\t: getMemberStat(name, 'effect-method', signalsAndMemos)\n\n\tstat.finalize = function (this: unknown) {\n\t\teffectifyIfNeeded(this as AnyObject, name, stat)\n\t}\n\n\tcontext.addInitializer(function () {\n\t\tfinalizeMembersIfLast(this as AnyObject, signalsAndMemos)\n\t})\n\n\tif (kind === 'method') stat.value = value\n\telse if (kind === 'accessor') stat.value = (value as ClassAccessorDecoratorTarget void>).get\n}\n\n/**\n * Starts all Solid.js effects created by the `@effect` decorator on the given\n * object. This can be used to restart effects that were previously stopped with\n * `stopEffects()`.\n *\n * Effects are created and started automatically, so this only needs to be\n * called if you have previously stopped the effects and want to start them\n * again.\n */\nexport function startEffects(obj: object) {\n\tlet effects = effects__.get(obj as AnyObject)\n\tif (!effects) return\n\teffects.startEffects()\n}\n\n/**\n * Stops all Solid.js effects created by the `@effect` decorator on the given\n * object. Use this once you are done with the instance and need to clean up.\n *\n * This does not needed to be used if the object is created in a reactive\n * context (such as inside a Solid.js component, or a nested effect), as those\n * effects will be cleaned up automatically when the owner context is cleaned\n * up.\n *\n * Effects that have been stopped can later be restarted by calling\n * `startEffects(obj)`.\n */\nexport function stopEffects(obj: object) {\n\tconst effects = effects__.get(obj as AnyObject)\n\tif (!effects) return\n\teffects.stopEffects()\n}\n"],"mappings":"AAAA,SAAQA,iBAAiB,EAAEC,qBAAqB,EAAEC,aAAa,EAAEC,UAAU,EAAEC,SAAS,QAAO,cAAc;AAE3G,OAAO,oBAAoB;;AAE3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAEA,OAAO,SAASC,MAAMA,CACrBC,KAAmE,EACnEC,OAAoE,EACnE;EACD,IAAIA,OAAO,CAACC,MAAM,EAAE,MAAM,IAAIC,KAAK,CAAC,6CAA6C,CAAC;EAElF,MAAM;IAACC,IAAI;IAAEC;EAAI,CAAC,GAAGJ,OAAO;EAC5B,MAAMK,QAAQ,GAAGL,OAAO,CAACK,QAA+B;EACxD,MAAMC,eAAe,GAAGV,UAAU,CAACS,QAAQ,CAAC;EAE5C,IAAI,EAAEF,IAAI,KAAK,QAAQ,IAAIA,IAAI,KAAK,UAAU,CAAC,EAC9C,MAAM,IAAID,KAAK,CAAC,kEAAkE,CAAC;EAEpF,MAAMK,IAAI,GACTJ,IAAI,KAAK,UAAU,GAChBR,aAAa,CAACS,IAAI,EAAE,sBAAsB,EAAEE,eAAe,CAAC,GAC5DX,aAAa,CAACS,IAAI,EAAE,eAAe,EAAEE,eAAe,CAAC;EAEzDC,IAAI,CAACC,QAAQ,GAAG,YAAyB;IACxCf,iBAAiB,CAAC,IAAI,EAAeW,IAAI,EAAEG,IAAI,CAAC;EACjD,CAAC;EAEDP,OAAO,CAACS,cAAc,CAAC,YAAY;IAClCf,qBAAqB,CAAC,IAAI,EAAeY,eAAe,CAAC;EAC1D,CAAC,CAAC;EAEF,IAAIH,IAAI,KAAK,QAAQ,EAAEI,IAAI,CAACR,KAAK,GAAGA,KAAK,MACpC,IAAII,IAAI,KAAK,UAAU,EAAEI,IAAI,CAACR,KAAK,GAAIA,KAAK,CAAuDW,GAAG;AAC5G;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,YAAYA,CAACC,GAAW,EAAE;EACzC,IAAIC,OAAO,GAAGhB,SAAS,CAACa,GAAG,CAACE,GAAgB,CAAC;EAC7C,IAAI,CAACC,OAAO,EAAE;EACdA,OAAO,CAACF,YAAY,CAAC,CAAC;AACvB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASG,WAAWA,CAACF,GAAW,EAAE;EACxC,MAAMC,OAAO,GAAGhB,SAAS,CAACa,GAAG,CAACE,GAAgB,CAAC;EAC/C,IAAI,CAACC,OAAO,EAAE;EACdA,OAAO,CAACC,WAAW,CAAC,CAAC;AACtB","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/effect.test.d.ts b/dist/decorators/effect.test.d.ts new file mode 100644 index 0000000..31ea660 --- /dev/null +++ b/dist/decorators/effect.test.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=effect.test.d.ts.map \ No newline at end of file diff --git a/dist/decorators/effect.test.d.ts.map b/dist/decorators/effect.test.d.ts.map new file mode 100644 index 0000000..c2233cd --- /dev/null +++ b/dist/decorators/effect.test.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"effect.test.d.ts","sourceRoot":"","sources":["../../src/decorators/effect.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/decorators/effect.test.js b/dist/decorators/effect.test.js new file mode 100644 index 0000000..cee01d2 --- /dev/null +++ b/dist/decorators/effect.test.js @@ -0,0 +1,897 @@ +function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { createSignal, batch, createRoot, createEffect } from 'solid-js'; +import { signal } from './signal.js'; +import { memo } from './memo.js'; +import { effect, startEffects, stopEffects } from './effect.js'; +import { Effects } from '../mixins/Effectful.js'; +import { testElementEffects } from '../index.test.js'; +describe('classy-solid', () => { + describe('@effect decorator', () => { + it('runs a basic method effect with signals using stopEffects', () => { + let _initProto, _init_b, _init_extra_b; + const [a, setA] = createSignal(1); + let last = null; + let runs = 0; + class Funkalicious { + static { + [_init_b, _init_extra_b, _initProto] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 2, "logSum"]]).e; + } + constructor() { + _init_extra_b(this); + } + b = (_initProto(this), _init_b(this, 2)); + logSum() { + runs++; + last = a() + this.b; + } + } + const fun = new Funkalicious(); + expect(last).toBe(1 + 2); + expect(runs).toBe(1); + setA(5); + expect(last).toBe(5 + 2); + expect(runs).toBe(2); + fun.b = 10; + expect(last).toBe(5 + 10); + expect(runs).toBe(3); + stopEffects(fun); + setA(1); + fun.b = 1; + expect(last).toBe(5 + 10); + expect(runs).toBe(3); + startEffects(fun); + expect(last).toBe(1 + 1); + expect(runs).toBe(4); + + // Ensure no duplicate effects + startEffects(fun); + expect(last).toBe(1 + 1); + expect(runs).toBe(4); + setA(3); + expect(last).toBe(3 + 1); + expect(runs).toBe(5); + stopEffects(fun); + setA(10); + fun.b = 20; + expect(last).toBe(3 + 1); + expect(runs).toBe(5); + }); + it('runs a basic method effect with signals using Effects', () => { + let _initProto2, _init_b2, _init_extra_b2; + const [a, setA] = createSignal(1); + let last = null; + let runs = 0; + class Funkalicious extends Effects { + static { + [_init_b2, _init_extra_b2, _initProto2] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 2, "logSum"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _init_extra_b2(this); + } + b = (_initProto2(this), _init_b2(this, 2)); + logSum() { + runs++; + last = a() + this.b; + } + } + const fun = new Funkalicious(); + expect(last).toBe(3); + expect(runs).toBe(1); + setA(5); + expect(last).toBe(7); + expect(runs).toBe(2); + fun.b = 10; + expect(last).toBe(15); + expect(runs).toBe(3); + fun.stopEffects(); + setA(1); + fun.b = 1; + expect(last).toBe(15); + expect(runs).toBe(3); + }); + it('runs multiple effects independently using Effects', () => { + let _initProto3, _init_b3, _init_extra_b3, _init_eff, _init_extra_eff; + const [a, setA] = createSignal(1); + let sum1 = 0; + let sum2 = 0; + let runs = 0; + class Doubler extends Effects { + static { + [_init_eff, _init_extra_eff, _init_b3, _init_extra_b3, _initProto3] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 2, "eff1"], [effect, 1, "eff2"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _init_extra_eff(this); + } + b = (_initProto3(this), _init_b3(this, 3)); + eff1() { + runs++; + sum1 = a() + this.b; + } + #A = (_init_extra_b3(this), _init_eff(this, () => { + runs++; + sum2 = (a() + this.b) * 2; + })); + get eff2() { + return this.#A; + } + set eff2(v) { + this.#A = v; + } + } + const o = new Doubler(); + expect(sum1).toBe(4); + expect(sum2).toBe(8); + expect(runs).toBe(2); + setA(2); + expect(sum1).toBe(5); + expect(sum2).toBe(10); + expect(runs).toBe(4); + o.b = 4; + expect(sum1).toBe(6); + expect(sum2).toBe(12); + expect(runs).toBe(6); + o.stopEffects(); + setA(10); + o.b = 20; + expect(sum1).toBe(6); + expect(sum2).toBe(12); + expect(runs).toBe(6); + }); + it('runs multiple effects independently using stopEffects', () => { + let _initProto4, _init_b4, _init_extra_b4, _init_eff2, _init_extra_eff2; + const [a, setA] = createSignal(1); + let sum1 = 0; + let sum2 = 0; + let runs = 0; + class Doubler { + static { + [_init_eff2, _init_extra_eff2, _init_b4, _init_extra_b4, _initProto4] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 2, "eff1"], [effect, 1, "eff2"]]).e; + } + constructor() { + _init_extra_eff2(this); + } + b = (_initProto4(this), _init_b4(this, 3)); + eff1() { + runs++; + sum1 = a() + this.b; + } + #A = (_init_extra_b4(this), _init_eff2(this, () => { + runs++; + sum2 = (a() + this.b) * 2; + })); + get eff2() { + return this.#A; + } + set eff2(v) { + this.#A = v; + } + } + const o = new Doubler(); + expect(sum1).toBe(4); + expect(sum2).toBe(8); + expect(runs).toBe(2); + setA(2); + expect(sum1).toBe(5); + expect(sum2).toBe(10); + expect(runs).toBe(4); + o.b = 4; + expect(sum1).toBe(6); + expect(sum2).toBe(12); + expect(runs).toBe(6); + stopEffects(o); + setA(10); + o.b = 20; + expect(sum1).toBe(6); + expect(sum2).toBe(12); + expect(runs).toBe(6); + }); + it('reruns effect when memos change inside effect using Effects', () => { + let _initProto5; + const [a, setA] = createSignal(1); + const [b, setB] = createSignal(2); + let memoVal = 0; + let effectRuns = 0; + class MemoUser extends Effects { + static { + [_initProto5] = _applyDecs(this, [], [[memo, 3, "sum"], [effect, 2, "report"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _initProto5(this); + } + get sum() { + return a() + b(); + } + report() { + effectRuns++; + memoVal = this.sum; + } + } + const m = new MemoUser(); + expect(memoVal).toBe(3); + expect(effectRuns).toBe(1); + setA(5); + expect(memoVal).toBe(7); + expect(effectRuns).toBe(2); + batch(() => { + setA(6); + setB(1); + }); // sum stays 7 + expect(effectRuns).toBe(2); + setB(5); + expect(memoVal).toBe(11); + expect(effectRuns).toBe(3); + m.stopEffects(); + setA(0); + setB(0); + expect(memoVal).toBe(11); + expect(effectRuns).toBe(3); + }); + it('reruns effect when memos change inside effect using stopEffects', () => { + let _initProto6; + const [a, setA] = createSignal(1); + const [b, setB] = createSignal(2); + let memoVal = 0; + let effectRuns = 0; + class MemoUser { + static { + [_initProto6] = _applyDecs(this, [], [[memo, 3, "sum"], [effect, 2, "report"]]).e; + } + constructor() { + _initProto6(this); + } + get sum() { + return a() + b(); + } + report() { + effectRuns++; + memoVal = this.sum; + } + } + const m = new MemoUser(); + expect(memoVal).toBe(3); + expect(effectRuns).toBe(1); + setA(5); + expect(memoVal).toBe(7); + expect(effectRuns).toBe(2); + batch(() => { + setA(6); + setB(1); + }); // sum stays 7 + expect(effectRuns).toBe(2); + setB(5); + expect(memoVal).toBe(11); + expect(effectRuns).toBe(3); + stopEffects(m); + setA(0); + setB(0); + expect(memoVal).toBe(11); + }); + it('runs an effect on auto accessor using Effects', () => { + let _init_b5, _init_extra_b5, _init_compute, _init_extra_compute; + const [a, setA] = createSignal(1); + class AccessorClass extends Effects { + static { + [_init_compute, _init_extra_compute, _init_b5, _init_extra_b5] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 1, "compute"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _init_extra_compute(this); + } + b = _init_b5(this, 2); + + // Stick this here to ensure that nested constructor doesn't + // interfere with decorator behavior mid-way through initialization + // of the wrapper parent class (tested with a subclass) + child = (_init_extra_b5(this), this.constructor !== AccessorClass ? new AccessorClass() : null); + result = 0; + runs = 0; + #A = _init_compute(this, () => { + this.runs++; + this.result = a() + this.b; + }); + get compute() { + return this.#A; + } + set compute(v) { + this.#A = v; + } + } + class Sub extends AccessorClass {} + const o = new Sub(); + expect(o.result).toBe(3); + expect(o.runs).toBe(1); + setA(5); + expect(o.result).toBe(7); + expect(o.runs).toBe(2); + o.b = 10; + expect(o.result).toBe(15); + expect(o.runs).toBe(3); + o.stopEffects(); + setA(1); + o.b = 1; + expect(o.result).toBe(15); + expect(o.runs).toBe(3); + }); + it('runs an effect on auto accessor using stopEffects', () => { + let _init_b6, _init_extra_b6, _init_compute2, _init_extra_compute2; + const [a, setA] = createSignal(1); + class AccessorClass { + static { + [_init_compute2, _init_extra_compute2, _init_b6, _init_extra_b6] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 1, "compute"]]).e; + } + constructor() { + _init_extra_compute2(this); + } + b = _init_b6(this, 2); + + // Stick this here to ensure that nested constructor doesn't + // interfere with decorator behavior mid-way through initialization + // of the wrapper parent class (tested with a subclass) + child = (_init_extra_b6(this), this.constructor !== AccessorClass ? new AccessorClass() : null); + result = 0; + runs = 0; + #A = _init_compute2(this, () => { + this.runs++; + this.result = a() + this.b; + }); + get compute() { + return this.#A; + } + set compute(v) { + this.#A = v; + } + } + class Sub extends AccessorClass {} + const o = new Sub(); + expect(o.result).toBe(3); + expect(o.runs).toBe(1); + setA(5); + expect(o.result).toBe(7); + expect(o.runs).toBe(2); + o.b = 10; + expect(o.result).toBe(15); + expect(o.runs).toBe(3); + stopEffects(o); + setA(1); + o.b = 1; + expect(o.result).toBe(15); + expect(o.runs).toBe(3); + }); + it('managed within an existing root, without Effects, without stopEffects', () => { + let _initProto7, _init_b7, _init_extra_b7; + const [a, setA] = createSignal(1); + let observed = 0; + let runs = 0; + class PlainYogurt { + static { + [_init_b7, _init_extra_b7, _initProto7] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 2, "sum"]]).e; + } + constructor() { + _init_extra_b7(this); + } + b = (_initProto7(this), _init_b7(this, 2)); + sum() { + runs++; + observed = a() + this.b; + } + } + let p; + let dispose; + createRoot(d => { + p = new PlainYogurt(); + dispose = d; + }); + + // As p is created inside a root, it will be tied to that root's owner, + // so this stopEffects(p) will not dispose the effects. + stopEffects(p); + expect(observed).toBe(3); + expect(runs).toBe(1); + setA(4); + p.b = 5; + expect(observed).toBe(9); + expect(runs).toBe(3); + + // Now dispose the root to clean up effects + dispose(); + setA(10); + p.b = 20; + expect(observed).toBe(9); // disposed root, no further updates + expect(runs).toBe(3); + }); + describe('subclass effect overriding/extending', () => { + it('runs subclass effect auto accessor extending base effect auto accessor with super', () => { + let _init_b8, _init_extra_b8, _init_eff3, _init_extra_eff3, _init_eff4, _init_extra_eff4; + const [a, setA] = createSignal(1); + let baseRuns = 0; + let subRuns = 0; + let observed = 0; + class Base extends Effects { + static { + [_init_eff3, _init_extra_eff3, _init_b8, _init_extra_b8] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 1, "eff"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _init_extra_eff3(this); + } + b = _init_b8(this, 2); + #A = (_init_extra_b8(this), _init_eff3(this, () => { + baseRuns++; + observed = a() + this.b; + })); + get eff() { + return this.#A; + } + set eff(v) { + this.#A = v; + } + } + class Sub extends Base { + static { + [_init_eff4, _init_extra_eff4] = _applyDecs(this, [], [[effect, 1, "eff"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _init_extra_eff4(this); + } + #A = _init_eff4(this, () => { + subRuns++; + super.eff(); + observed = observed + 10; + }); + get eff() { + return this.#A; + } + set eff(v) { + this.#A = v; + } + } + const o = new Sub(); + expect(baseRuns).toBe(1); + expect(subRuns).toBe(1); + expect(observed).toBe(1 + 2 + 10); + o.b = 5; + expect(baseRuns).toBe(2); + expect(subRuns).toBe(2); + expect(observed).toBe(1 + 5 + 10); + setA(10); + expect(baseRuns).toBe(3); + expect(subRuns).toBe(3); + expect(observed).toBe(10 + 5 + 10); + o.stopEffects(); + o.b = 100; + expect(baseRuns).toBe(3); + expect(subRuns).toBe(3); + expect(observed).toBe(10 + 5 + 10); + }); + it('runs subclass effect auto accessor overriding base effect auto accessor without super', () => { + let _init_b9, _init_extra_b9, _init_eff5, _init_extra_eff5, _init_eff6, _init_extra_eff6; + const [a, setA] = createSignal(1); + let baseRuns = 0; + let subRuns = 0; + let observed = 0; + class Base extends Effects { + static { + [_init_eff5, _init_extra_eff5, _init_b9, _init_extra_b9] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 1, "eff"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _init_extra_eff5(this); + } + b = _init_b9(this, 2); + #A = (_init_extra_b9(this), _init_eff5(this, () => { + baseRuns++; + observed = a() + this.b; + })); + get eff() { + return this.#A; + } + set eff(v) { + this.#A = v; + } + } + class Sub extends Base { + static { + [_init_eff6, _init_extra_eff6] = _applyDecs(this, [], [[effect, 1, "eff"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _init_extra_eff6(this); + } + #A = _init_eff6(this, () => { + subRuns++; + observed = (a() + this.b) * 2; // override without super + }); + get eff() { + return this.#A; + } + set eff(v) { + this.#A = v; + } + } + const o = new Sub(); + expect(baseRuns).toBe(0); + expect(subRuns).toBe(1); + expect(observed).toBe((1 + 2) * 2); + o.b = 5; + expect(baseRuns).toBe(0); + expect(subRuns).toBe(2); + expect(observed).toBe((1 + 5) * 2); + setA(10); + expect(baseRuns).toBe(0); + expect(subRuns).toBe(3); + expect(observed).toBe((10 + 5) * 2); + o.stopEffects(); + o.b = 100; + expect(baseRuns).toBe(0); + expect(subRuns).toBe(3); + expect(observed).toBe((10 + 5) * 2); + }); + it('runs subclass effect method extending base effect method with super', () => { + let _initProto8, _init_b0, _init_extra_b0, _initProto9, _init_c, _init_extra_c; + const [a, setA] = createSignal(1); + let superRuns = 0; + let subRuns = 0; + let observed = 0; + class Base extends Effects { + static { + [_init_b0, _init_extra_b0, _initProto8] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 2, "compute"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _init_extra_b0(this); + } + b = (_initProto8(this), _init_b0(this, 2)); + compute() { + superRuns++; + observed = a() + this.b; + } + } + class Sub extends Base { + static { + [_init_c, _init_extra_c, _initProto9] = _applyDecs(this, [], [[signal, 0, "c"], [effect, 2, "compute"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _init_extra_c(this); + } + c = (_initProto9(this), _init_c(this, 3)); + compute() { + subRuns++; + super.compute(); + observed += this.c; // extend behavior + } + } + const o = new Sub(); + expect(superRuns).toBe(1); + expect(subRuns).toBe(1); + expect(observed).toBe(1 + 2 + 3); // a + b + extension + + setA(5); + expect(superRuns).toBe(2); + expect(subRuns).toBe(2); + expect(observed).toBe(5 + 2 + 3); + o.b = 10; + expect(superRuns).toBe(3); + expect(subRuns).toBe(3); + expect(observed).toBe(5 + 10 + 3); + o.c = 5; + expect(superRuns).toBe(4); + expect(subRuns).toBe(4); + expect(observed).toBe(5 + 10 + 5); + o.stopEffects(); + setA(0); + o.b = 0; + o.c = 0; + expect(superRuns).toBe(4); + expect(subRuns).toBe(4); + expect(observed).toBe(5 + 10 + 5); + }); + it('supports multi-level effect method extending base effect method with super', () => { + let _initProto0, _init_b1, _init_extra_b1, _initProto1, _init_c2, _init_extra_c2, _initProto10, _init_d, _init_extra_d; + const [a, setA] = createSignal(1); + let baseRuns = 0; + let midRuns = 0; + let subRuns = 0; + let observed = 0; + class Base extends Effects { + static { + [_init_b1, _init_extra_b1, _initProto0] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 2, "compute"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _init_extra_b1(this); + } + b = (_initProto0(this), _init_b1(this, 2)); + compute() { + baseRuns++; + observed = a() + this.b; + } + } + class Mid extends Base { + static { + [_init_c2, _init_extra_c2, _initProto1] = _applyDecs(this, [], [[signal, 0, "c"], [effect, 2, "compute"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _init_extra_c2(this); + } + c = (_initProto1(this), _init_c2(this, 3)); + compute() { + midRuns++; + super.compute(); + observed += this.c; + } + } + class Sub extends Mid { + static { + [_init_d, _init_extra_d, _initProto10] = _applyDecs(this, [], [[signal, 0, "d"], [effect, 2, "compute"]], 0, void 0, Mid).e; + } + constructor(...args) { + super(...args); + _init_extra_d(this); + } + d = (_initProto10(this), _init_d(this, 4)); + compute() { + subRuns++; + super.compute(); + observed += this.d; + } + } + const o = new Sub(); + expect(baseRuns).toBe(1); + expect(midRuns).toBe(1); + expect(subRuns).toBe(1); + expect(observed).toBe(1 + 2 + 3 + 4); + setA(5); + expect(baseRuns).toBe(2); + expect(midRuns).toBe(2); + expect(subRuns).toBe(2); + expect(observed).toBe(5 + 2 + 3 + 4); + o.b = 10; + expect(baseRuns).toBe(3); + expect(midRuns).toBe(3); + expect(subRuns).toBe(3); + expect(observed).toBe(5 + 10 + 3 + 4); + o.c = 6; + expect(baseRuns).toBe(4); + expect(midRuns).toBe(4); + expect(subRuns).toBe(4); + expect(observed).toBe(5 + 10 + 6 + 4); + o.d = 7; + expect(baseRuns).toBe(5); + expect(midRuns).toBe(5); + expect(subRuns).toBe(5); + expect(observed).toBe(5 + 10 + 6 + 7); + o.stopEffects(); + setA(0); + o.b = 0; + o.c = 0; + o.d = 0; + expect(baseRuns).toBe(5); + expect(midRuns).toBe(5); + expect(subRuns).toBe(5); + expect(observed).toBe(5 + 10 + 6 + 7); + }); + it('runs subclass effect method overriding base effect method without super', () => { + let _initProto11, _init_b10, _init_extra_b10, _initProto12; + const [a, setA] = createSignal(1); + let superRuns = 0; + let subRuns = 0; + let observed = 0; + class Base extends Effects { + static { + [_init_b10, _init_extra_b10, _initProto11] = _applyDecs(this, [], [[signal, 0, "b"], [effect, 2, "compute"]], 0, void 0, Effects).e; + } + constructor(...args) { + super(...args); + _init_extra_b10(this); + } + b = (_initProto11(this), _init_b10(this, 2)); + compute() { + superRuns++; + observed = a() + this.b; + } + } + class Sub extends Base { + static { + [_initProto12] = _applyDecs(this, [], [[effect, 2, "compute"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _initProto12(this); + } + compute() { + subRuns++; + observed = (a() + this.b) * 2; // override without super + } + } + const o = new Sub(); + expect(superRuns).toBe(0); + expect(subRuns).toBe(1); + expect(observed).toBe((1 + 2) * 2); + setA(3); + expect(superRuns).toBe(0); + expect(subRuns).toBe(2); + expect(observed).toBe((3 + 2) * 2); + o.b = 5; + expect(superRuns).toBe(0); + expect(subRuns).toBe(3); + expect(observed).toBe((3 + 5) * 2); + o.stopEffects(); + setA(10); + o.b = 1; + expect(subRuns).toBe(3); + }); + }); + it('works with nested effects', () => { + let _initProto13, _init_a, _init_extra_a, _init_b11, _init_extra_b11; + let outerRuns = 0; + let innerRuns = 0; + class MyEffects { + static { + [_init_a, _init_extra_a, _init_b11, _init_extra_b11, _initProto13] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [effect, 2, "outer"]]).e; + } + constructor() { + _init_extra_b11(this); + } + a = (_initProto13(this), _init_a(this, 0)); + b = (_init_extra_a(this), _init_b11(this, 0)); + outer() { + outerRuns++; + this.a; + createEffect(() => { + innerRuns++; + this.b; + }); + } + } + const e = new MyEffects(); + expect(outerRuns).toBe(1); + expect(innerRuns).toBe(1); + startEffects(e); // should not duplicate effects (already started) + + expect(outerRuns).toBe(1); + expect(innerRuns).toBe(1); + e.a = 1; + expect(outerRuns).toBe(2); + expect(innerRuns).toBe(2); // inner effect runs because outer effect re-ran + + e.b = 1; + expect(outerRuns).toBe(2); + expect(innerRuns).toBe(3); // inner effect runs independently + + stopEffects(e); + expect(outerRuns).toBe(2); + expect(innerRuns).toBe(3); + startEffects(e); + expect(outerRuns).toBe(3); + expect(innerRuns).toBe(4); // inner effect runs because outer effect re-ran + + e.b = 2; + expect(outerRuns).toBe(3); + expect(innerRuns).toBe(5); // inner effect runs independently + }); + describe('invalid usage', () => { + it('throws on invalid field usage', () => { + expect(() => { + let _init_nope, _init_extra_nope; + class BadField { + static { + [_init_nope, _init_extra_nope] = _applyDecs(this, [], [[effect, 0, "nope"]]).e; + } + constructor() { + _init_extra_nope(this); + } + // @ts-expect-error invalid decorator usage on field + nope = _init_nope(this, () => 123); + } + new BadField(); + }).toThrow('@effect can only be used on methods or function-valued accessors'); + }); + it('throws on invalid getter usage', () => { + expect(() => { + let _initProto14, _init_a2, _init_extra_a2; + class BadGetter { + static { + [_init_a2, _init_extra_a2, _initProto14] = _applyDecs(this, [], [[signal, 0, "a"], [effect, 3, "nope"]]).e; + } + constructor() { + _init_extra_a2(this); + } + a = (_initProto14(this), _init_a2(this, 1)); + // @ts-expect-error invalid decorator usage on getter + get nope() { + return this.a; + } + } + new BadGetter(); + }).toThrow('@effect can only be used on methods or function-valued accessors'); + }); + it('throws on invalid static usage', () => { + expect(() => { + let _initStatic; + class BadStatic { + static { + [_initStatic] = _applyDecs(this, [], [[effect, 10, "nope"]]).e; + _initStatic(this); + } + static nope() {} + } + BadStatic; + }).toThrow('@effect is not supported on static members.'); + }); + it('throws on invalid non-function value', () => { + expect(() => { + let _init_a3, _init_extra_a3, _init_nope2, _init_extra_nope2; + class NonFunction { + static { + [_init_nope2, _init_extra_nope2, _init_a3, _init_extra_a3] = _applyDecs(this, [], [[signal, 0, "a"], [effect, 1, "nope"]]).e; + } + constructor() { + _init_extra_nope2(this); + } + a = _init_a3(this, 1); + // @ts-expect-error invalid decorator usage on non-function + #A = (_init_extra_a3(this), _init_nope2(this, 123)); + get nope() { + return this.#A; + } + set nope(v) { + this.#A = v; + } + } + new NonFunction(); + }).toThrow('@effect decorated member "nope" is not a function'); + }); + it('throws on duplicate members', () => { + const run = () => { + let _initProto15; + class SuperDuper { + static { + [_initProto15] = _applyDecs(this, [], [[effect, 2, "dupe"], [effect, 2, "dupe"]]).e; + } + constructor() { + _initProto15(this); + } + // @ts-expect-error duplicate member + dupe() { + this; + } + + // @ts-expect-error duplicate member + dupe() { + this; + } + } + new SuperDuper(); + }; + + // When compiling with Babel, decorators currently throw an error when applied onto multiple members of the same name. + expect(run).toThrow('Decorating two elements with the same name (dupe) is not supported yet'); + + // When compiling with TypeScript, decorating duplicate members is allowed, and the last one wins. + // expect(run).toThrow( + // '@effect decorated member "dupe" has already been effectified. This can happen if there are duplicated class members.', + // ) + }); + }); + describe('usage with custom elements', () => { + it('@effect methods, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => { + const el = document.createElement('my-element4'); + expect(el.result).toBe(1 + 2); + expect(el.runs).toBe(1); // already ran in constructor + + testElementEffects(el); + }); + }); + }); +}); +//# sourceMappingURL=effect.test.js.map \ No newline at end of file diff --git a/dist/decorators/effect.test.js.map b/dist/decorators/effect.test.js.map new file mode 100644 index 0000000..cf4cd88 --- /dev/null +++ b/dist/decorators/effect.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"effect.test.js","names":["createSignal","batch","createRoot","createEffect","signal","memo","effect","startEffects","stopEffects","Effects","testElementEffects","describe","it","_initProto","_init_b","_init_extra_b","a","setA","last","runs","Funkalicious","_applyDecs","e","constructor","b","logSum","fun","expect","toBe","_initProto2","_init_b2","_init_extra_b2","args","_initProto3","_init_b3","_init_extra_b3","_init_eff","_init_extra_eff","sum1","sum2","Doubler","eff1","A","eff2","v","o","_initProto4","_init_b4","_init_extra_b4","_init_eff2","_init_extra_eff2","_initProto5","setB","memoVal","effectRuns","MemoUser","sum","report","m","_initProto6","_init_b5","_init_extra_b5","_init_compute","_init_extra_compute","AccessorClass","child","result","compute","Sub","_init_b6","_init_extra_b6","_init_compute2","_init_extra_compute2","_initProto7","_init_b7","_init_extra_b7","observed","PlainYogurt","p","dispose","d","_init_b8","_init_extra_b8","_init_eff3","_init_extra_eff3","_init_eff4","_init_extra_eff4","baseRuns","subRuns","Base","eff","_init_b9","_init_extra_b9","_init_eff5","_init_extra_eff5","_init_eff6","_init_extra_eff6","_initProto8","_init_b0","_init_extra_b0","_initProto9","_init_c","_init_extra_c","superRuns","c","_initProto0","_init_b1","_init_extra_b1","_initProto1","_init_c2","_init_extra_c2","_initProto10","_init_d","_init_extra_d","midRuns","Mid","_initProto11","_init_b10","_init_extra_b10","_initProto12","_initProto13","_init_a","_init_extra_a","_init_b11","_init_extra_b11","outerRuns","innerRuns","MyEffects","outer","_init_nope","_init_extra_nope","BadField","nope","toThrow","_initProto14","_init_a2","_init_extra_a2","BadGetter","_initStatic","BadStatic","_init_a3","_init_extra_a3","_init_nope2","_init_extra_nope2","NonFunction","run","_initProto15","SuperDuper","dupe","el","document","createElement"],"sources":["../../src/decorators/effect.test.ts"],"sourcesContent":["import {createSignal, batch, createRoot, createEffect} from 'solid-js'\nimport {signal} from './signal.js'\nimport {memo} from './memo.js'\nimport {effect, startEffects, stopEffects} from './effect.js'\nimport {Effects} from '../mixins/Effectful.js'\nimport {testElementEffects, type MyElement4} from '../index.test.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('@effect decorator', () => {\n\t\tit('runs a basic method effect with signals using stopEffects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tlet last: number | null = null\n\t\t\tlet runs = 0\n\n\t\t\tclass Funkalicious {\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@effect logSum() {\n\t\t\t\t\truns++\n\t\t\t\t\tlast = a() + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst fun = new Funkalicious()\n\t\t\texpect(last).toBe(1 + 2)\n\t\t\texpect(runs).toBe(1)\n\n\t\t\tsetA(5)\n\t\t\texpect(last).toBe(5 + 2)\n\t\t\texpect(runs).toBe(2)\n\n\t\t\tfun.b = 10\n\t\t\texpect(last).toBe(5 + 10)\n\t\t\texpect(runs).toBe(3)\n\n\t\t\tstopEffects(fun)\n\t\t\tsetA(1)\n\t\t\tfun.b = 1\n\t\t\texpect(last).toBe(5 + 10)\n\t\t\texpect(runs).toBe(3)\n\n\t\t\tstartEffects(fun)\n\t\t\texpect(last).toBe(1 + 1)\n\t\t\texpect(runs).toBe(4)\n\n\t\t\t// Ensure no duplicate effects\n\t\t\tstartEffects(fun)\n\t\t\texpect(last).toBe(1 + 1)\n\t\t\texpect(runs).toBe(4)\n\n\t\t\tsetA(3)\n\t\t\texpect(last).toBe(3 + 1)\n\t\t\texpect(runs).toBe(5)\n\n\t\t\tstopEffects(fun)\n\t\t\tsetA(10)\n\t\t\tfun.b = 20\n\t\t\texpect(last).toBe(3 + 1)\n\t\t\texpect(runs).toBe(5)\n\t\t})\n\n\t\tit('runs a basic method effect with signals using Effects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tlet last: number | null = null\n\t\t\tlet runs = 0\n\n\t\t\tclass Funkalicious extends Effects {\n\t\t\t\t@signal b = 2\n\t\t\t\t@effect logSum() {\n\t\t\t\t\truns++\n\t\t\t\t\tlast = a() + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst fun = new Funkalicious()\n\t\t\texpect(last).toBe(3)\n\t\t\texpect(runs).toBe(1)\n\n\t\t\tsetA(5)\n\t\t\texpect(last).toBe(7)\n\t\t\texpect(runs).toBe(2)\n\n\t\t\tfun.b = 10\n\t\t\texpect(last).toBe(15)\n\t\t\texpect(runs).toBe(3)\n\n\t\t\tfun.stopEffects()\n\t\t\tsetA(1)\n\t\t\tfun.b = 1\n\t\t\texpect(last).toBe(15)\n\t\t\texpect(runs).toBe(3)\n\t\t})\n\n\t\tit('runs multiple effects independently using Effects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tlet sum1 = 0\n\t\t\tlet sum2 = 0\n\t\t\tlet runs = 0\n\n\t\t\tclass Doubler extends Effects {\n\t\t\t\t@signal b = 3\n\t\t\t\t@effect eff1() {\n\t\t\t\t\truns++\n\t\t\t\t\tsum1 = a() + this.b\n\t\t\t\t}\n\t\t\t\t@effect accessor eff2 = () => {\n\t\t\t\t\truns++\n\t\t\t\t\tsum2 = (a() + this.b) * 2\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst o = new Doubler()\n\t\t\texpect(sum1).toBe(4)\n\t\t\texpect(sum2).toBe(8)\n\t\t\texpect(runs).toBe(2)\n\n\t\t\tsetA(2)\n\t\t\texpect(sum1).toBe(5)\n\t\t\texpect(sum2).toBe(10)\n\t\t\texpect(runs).toBe(4)\n\n\t\t\to.b = 4\n\t\t\texpect(sum1).toBe(6)\n\t\t\texpect(sum2).toBe(12)\n\t\t\texpect(runs).toBe(6)\n\n\t\t\to.stopEffects()\n\t\t\tsetA(10)\n\t\t\to.b = 20\n\t\t\texpect(sum1).toBe(6)\n\t\t\texpect(sum2).toBe(12)\n\t\t\texpect(runs).toBe(6)\n\t\t})\n\n\t\tit('runs multiple effects independently using stopEffects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tlet sum1 = 0\n\t\t\tlet sum2 = 0\n\t\t\tlet runs = 0\n\n\t\t\tclass Doubler {\n\t\t\t\t@signal b = 3\n\t\t\t\t@effect eff1() {\n\t\t\t\t\truns++\n\t\t\t\t\tsum1 = a() + this.b\n\t\t\t\t}\n\t\t\t\t@effect accessor eff2 = () => {\n\t\t\t\t\truns++\n\t\t\t\t\tsum2 = (a() + this.b) * 2\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst o = new Doubler()\n\t\t\texpect(sum1).toBe(4)\n\t\t\texpect(sum2).toBe(8)\n\t\t\texpect(runs).toBe(2)\n\n\t\t\tsetA(2)\n\t\t\texpect(sum1).toBe(5)\n\t\t\texpect(sum2).toBe(10)\n\t\t\texpect(runs).toBe(4)\n\n\t\t\to.b = 4\n\t\t\texpect(sum1).toBe(6)\n\t\t\texpect(sum2).toBe(12)\n\t\t\texpect(runs).toBe(6)\n\n\t\t\tstopEffects(o)\n\t\t\tsetA(10)\n\t\t\to.b = 20\n\t\t\texpect(sum1).toBe(6)\n\t\t\texpect(sum2).toBe(12)\n\t\t\texpect(runs).toBe(6)\n\t\t})\n\n\t\tit('reruns effect when memos change inside effect using Effects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tconst [b, setB] = createSignal(2)\n\t\t\tlet memoVal = 0\n\t\t\tlet effectRuns = 0\n\n\t\t\tclass MemoUser extends Effects {\n\t\t\t\t@memo get sum() {\n\t\t\t\t\treturn a() + b()\n\t\t\t\t}\n\t\t\t\t@effect report() {\n\t\t\t\t\teffectRuns++\n\t\t\t\t\tmemoVal = this.sum\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst m = new MemoUser()\n\t\t\texpect(memoVal).toBe(3)\n\t\t\texpect(effectRuns).toBe(1)\n\n\t\t\tsetA(5)\n\t\t\texpect(memoVal).toBe(7)\n\t\t\texpect(effectRuns).toBe(2)\n\n\t\t\tbatch(() => {\n\t\t\t\tsetA(6)\n\t\t\t\tsetB(1)\n\t\t\t}) // sum stays 7\n\t\t\texpect(effectRuns).toBe(2)\n\n\t\t\tsetB(5)\n\t\t\texpect(memoVal).toBe(11)\n\t\t\texpect(effectRuns).toBe(3)\n\n\t\t\tm.stopEffects()\n\t\t\tsetA(0)\n\t\t\tsetB(0)\n\t\t\texpect(memoVal).toBe(11)\n\t\t\texpect(effectRuns).toBe(3)\n\t\t})\n\n\t\tit('reruns effect when memos change inside effect using stopEffects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tconst [b, setB] = createSignal(2)\n\t\t\tlet memoVal = 0\n\t\t\tlet effectRuns = 0\n\n\t\t\tclass MemoUser {\n\t\t\t\t@memo get sum() {\n\t\t\t\t\treturn a() + b()\n\t\t\t\t}\n\t\t\t\t@effect report() {\n\t\t\t\t\teffectRuns++\n\t\t\t\t\tmemoVal = this.sum\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst m = new MemoUser()\n\t\t\texpect(memoVal).toBe(3)\n\t\t\texpect(effectRuns).toBe(1)\n\n\t\t\tsetA(5)\n\t\t\texpect(memoVal).toBe(7)\n\t\t\texpect(effectRuns).toBe(2)\n\n\t\t\tbatch(() => {\n\t\t\t\tsetA(6)\n\t\t\t\tsetB(1)\n\t\t\t}) // sum stays 7\n\t\t\texpect(effectRuns).toBe(2)\n\n\t\t\tsetB(5)\n\t\t\texpect(memoVal).toBe(11)\n\t\t\texpect(effectRuns).toBe(3)\n\n\t\t\tstopEffects(m)\n\t\t\tsetA(0)\n\t\t\tsetB(0)\n\t\t\texpect(memoVal).toBe(11)\n\t\t})\n\n\t\tit('runs an effect on auto accessor using Effects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\n\t\t\tclass AccessorClass extends Effects {\n\t\t\t\t@signal b = 2\n\n\t\t\t\t// Stick this here to ensure that nested constructor doesn't\n\t\t\t\t// interfere with decorator behavior mid-way through initialization\n\t\t\t\t// of the wrapper parent class (tested with a subclass)\n\t\t\t\tchild: AccessorClass | null = this.constructor !== AccessorClass ? new AccessorClass() : null\n\n\t\t\t\tresult = 0\n\t\t\t\truns = 0\n\t\t\t\t@effect accessor compute = () => {\n\t\t\t\t\tthis.runs++\n\t\t\t\t\tthis.result = a() + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclass Sub extends AccessorClass {}\n\n\t\t\tconst o = new Sub()\n\t\t\texpect(o.result).toBe(3)\n\t\t\texpect(o.runs).toBe(1)\n\n\t\t\tsetA(5)\n\t\t\texpect(o.result).toBe(7)\n\t\t\texpect(o.runs).toBe(2)\n\t\t\to.b = 10\n\t\t\texpect(o.result).toBe(15)\n\t\t\texpect(o.runs).toBe(3)\n\n\t\t\to.stopEffects()\n\t\t\tsetA(1)\n\t\t\to.b = 1\n\t\t\texpect(o.result).toBe(15)\n\t\t\texpect(o.runs).toBe(3)\n\t\t})\n\n\t\tit('runs an effect on auto accessor using stopEffects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\n\t\t\tclass AccessorClass {\n\t\t\t\t@signal b = 2\n\n\t\t\t\t// Stick this here to ensure that nested constructor doesn't\n\t\t\t\t// interfere with decorator behavior mid-way through initialization\n\t\t\t\t// of the wrapper parent class (tested with a subclass)\n\t\t\t\tchild: AccessorClass | null = this.constructor !== AccessorClass ? new AccessorClass() : null\n\n\t\t\t\tresult = 0\n\t\t\t\truns = 0\n\t\t\t\t@effect accessor compute = () => {\n\t\t\t\t\tthis.runs++\n\t\t\t\t\tthis.result = a() + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclass Sub extends AccessorClass {}\n\n\t\t\tconst o = new Sub()\n\t\t\texpect(o.result).toBe(3)\n\t\t\texpect(o.runs).toBe(1)\n\n\t\t\tsetA(5)\n\t\t\texpect(o.result).toBe(7)\n\t\t\texpect(o.runs).toBe(2)\n\n\t\t\to.b = 10\n\t\t\texpect(o.result).toBe(15)\n\t\t\texpect(o.runs).toBe(3)\n\n\t\t\tstopEffects(o)\n\t\t\tsetA(1)\n\t\t\to.b = 1\n\t\t\texpect(o.result).toBe(15)\n\t\t\texpect(o.runs).toBe(3)\n\t\t})\n\n\t\tit('managed within an existing root, without Effects, without stopEffects', () => {\n\t\t\tconst [a, setA] = createSignal(1)\n\t\t\tlet observed = 0\n\t\t\tlet runs = 0\n\n\t\t\tclass PlainYogurt {\n\t\t\t\t@signal b = 2\n\t\t\t\t@effect sum() {\n\t\t\t\t\truns++\n\t\t\t\t\tobserved = a() + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet p!: PlainYogurt\n\t\t\tlet dispose!: () => void\n\n\t\t\tcreateRoot(d => {\n\t\t\t\tp = new PlainYogurt()\n\t\t\t\tdispose = d\n\t\t\t})\n\n\t\t\t// As p is created inside a root, it will be tied to that root's owner,\n\t\t\t// so this stopEffects(p) will not dispose the effects.\n\t\t\tstopEffects(p)\n\n\t\t\texpect(observed).toBe(3)\n\t\t\texpect(runs).toBe(1)\n\n\t\t\tsetA(4)\n\t\t\tp.b = 5\n\n\t\t\texpect(observed).toBe(9)\n\t\t\texpect(runs).toBe(3)\n\n\t\t\t// Now dispose the root to clean up effects\n\t\t\tdispose()\n\t\t\tsetA(10)\n\t\t\tp.b = 20\n\t\t\texpect(observed).toBe(9) // disposed root, no further updates\n\t\t\texpect(runs).toBe(3)\n\t\t})\n\n\t\tdescribe('subclass effect overriding/extending', () => {\n\t\t\tit('runs subclass effect auto accessor extending base effect auto accessor with super', () => {\n\t\t\t\tconst [a, setA] = createSignal(1)\n\n\t\t\t\tlet baseRuns = 0\n\t\t\t\tlet subRuns = 0\n\t\t\t\tlet observed = 0\n\n\t\t\t\tclass Base extends Effects {\n\t\t\t\t\t@signal b = 2\n\t\t\t\t\t@effect accessor eff = () => {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\tobserved = a() + this.b\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@effect override accessor eff = () => {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\tsuper.eff()\n\t\t\t\t\t\tobserved = observed + 10\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst o = new Sub()\n\t\t\t\texpect(baseRuns).toBe(1)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(observed).toBe(1 + 2 + 10)\n\n\t\t\t\to.b = 5\n\t\t\t\texpect(baseRuns).toBe(2)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(observed).toBe(1 + 5 + 10)\n\n\t\t\t\tsetA(10)\n\t\t\t\texpect(baseRuns).toBe(3)\n\t\t\t\texpect(subRuns).toBe(3)\n\t\t\t\texpect(observed).toBe(10 + 5 + 10)\n\n\t\t\t\to.stopEffects()\n\t\t\t\to.b = 100\n\t\t\t\texpect(baseRuns).toBe(3)\n\t\t\t\texpect(subRuns).toBe(3)\n\t\t\t\texpect(observed).toBe(10 + 5 + 10)\n\t\t\t})\n\n\t\t\tit('runs subclass effect auto accessor overriding base effect auto accessor without super', () => {\n\t\t\t\tconst [a, setA] = createSignal(1)\n\n\t\t\t\tlet baseRuns = 0\n\t\t\t\tlet subRuns = 0\n\t\t\t\tlet observed = 0\n\n\t\t\t\tclass Base extends Effects {\n\t\t\t\t\t@signal b = 2\n\t\t\t\t\t@effect accessor eff = () => {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\tobserved = a() + this.b\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@effect override accessor eff = () => {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\tobserved = (a() + this.b) * 2 // override without super\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst o = new Sub()\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(observed).toBe((1 + 2) * 2)\n\n\t\t\t\to.b = 5\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(observed).toBe((1 + 5) * 2)\n\n\t\t\t\tsetA(10)\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(3)\n\t\t\t\texpect(observed).toBe((10 + 5) * 2)\n\n\t\t\t\to.stopEffects()\n\t\t\t\to.b = 100\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(3)\n\t\t\t\texpect(observed).toBe((10 + 5) * 2)\n\t\t\t})\n\n\t\t\tit('runs subclass effect method extending base effect method with super', () => {\n\t\t\t\tconst [a, setA] = createSignal(1)\n\t\t\t\tlet superRuns = 0\n\t\t\t\tlet subRuns = 0\n\t\t\t\tlet observed = 0\n\n\t\t\t\tclass Base extends Effects {\n\t\t\t\t\t@signal b = 2\n\t\t\t\t\t@effect compute() {\n\t\t\t\t\t\tsuperRuns++\n\t\t\t\t\t\tobserved = a() + this.b\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@signal c = 3\n\t\t\t\t\t@effect override compute() {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\tsuper.compute()\n\t\t\t\t\t\tobserved += this.c // extend behavior\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst o = new Sub()\n\t\t\t\texpect(superRuns).toBe(1)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(observed).toBe(1 + 2 + 3) // a + b + extension\n\n\t\t\t\tsetA(5)\n\t\t\t\texpect(superRuns).toBe(2)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(observed).toBe(5 + 2 + 3)\n\n\t\t\t\to.b = 10\n\t\t\t\texpect(superRuns).toBe(3)\n\t\t\t\texpect(subRuns).toBe(3)\n\t\t\t\texpect(observed).toBe(5 + 10 + 3)\n\n\t\t\t\to.c = 5\n\t\t\t\texpect(superRuns).toBe(4)\n\t\t\t\texpect(subRuns).toBe(4)\n\t\t\t\texpect(observed).toBe(5 + 10 + 5)\n\n\t\t\t\to.stopEffects()\n\t\t\t\tsetA(0)\n\t\t\t\to.b = 0\n\t\t\t\to.c = 0\n\t\t\t\texpect(superRuns).toBe(4)\n\t\t\t\texpect(subRuns).toBe(4)\n\t\t\t\texpect(observed).toBe(5 + 10 + 5)\n\t\t\t})\n\n\t\t\tit('supports multi-level effect method extending base effect method with super', () => {\n\t\t\t\tconst [a, setA] = createSignal(1)\n\t\t\t\tlet baseRuns = 0\n\t\t\t\tlet midRuns = 0\n\t\t\t\tlet subRuns = 0\n\t\t\t\tlet observed = 0\n\n\t\t\t\tclass Base extends Effects {\n\t\t\t\t\t@signal b = 2\n\t\t\t\t\t@effect compute() {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\tobserved = a() + this.b\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Mid extends Base {\n\t\t\t\t\t@signal c = 3\n\t\t\t\t\t@effect override compute() {\n\t\t\t\t\t\tmidRuns++\n\t\t\t\t\t\tsuper.compute()\n\t\t\t\t\t\tobserved += this.c\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Mid {\n\t\t\t\t\t@signal d = 4\n\t\t\t\t\t@effect override compute() {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\tsuper.compute()\n\t\t\t\t\t\tobserved += this.d\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst o = new Sub()\n\t\t\t\texpect(baseRuns).toBe(1)\n\t\t\t\texpect(midRuns).toBe(1)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(observed).toBe(1 + 2 + 3 + 4)\n\n\t\t\t\tsetA(5)\n\t\t\t\texpect(baseRuns).toBe(2)\n\t\t\t\texpect(midRuns).toBe(2)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(observed).toBe(5 + 2 + 3 + 4)\n\n\t\t\t\to.b = 10\n\t\t\t\texpect(baseRuns).toBe(3)\n\t\t\t\texpect(midRuns).toBe(3)\n\t\t\t\texpect(subRuns).toBe(3)\n\t\t\t\texpect(observed).toBe(5 + 10 + 3 + 4)\n\n\t\t\t\to.c = 6\n\t\t\t\texpect(baseRuns).toBe(4)\n\t\t\t\texpect(midRuns).toBe(4)\n\t\t\t\texpect(subRuns).toBe(4)\n\t\t\t\texpect(observed).toBe(5 + 10 + 6 + 4)\n\n\t\t\t\to.d = 7\n\t\t\t\texpect(baseRuns).toBe(5)\n\t\t\t\texpect(midRuns).toBe(5)\n\t\t\t\texpect(subRuns).toBe(5)\n\t\t\t\texpect(observed).toBe(5 + 10 + 6 + 7)\n\n\t\t\t\to.stopEffects()\n\t\t\t\tsetA(0)\n\t\t\t\to.b = 0\n\t\t\t\to.c = 0\n\t\t\t\to.d = 0\n\t\t\t\texpect(baseRuns).toBe(5)\n\t\t\t\texpect(midRuns).toBe(5)\n\t\t\t\texpect(subRuns).toBe(5)\n\t\t\t\texpect(observed).toBe(5 + 10 + 6 + 7)\n\t\t\t})\n\n\t\t\tit('runs subclass effect method overriding base effect method without super', () => {\n\t\t\t\tconst [a, setA] = createSignal(1)\n\t\t\t\tlet superRuns = 0\n\t\t\t\tlet subRuns = 0\n\t\t\t\tlet observed = 0\n\n\t\t\t\tclass Base extends Effects {\n\t\t\t\t\t@signal b = 2\n\t\t\t\t\t@effect compute() {\n\t\t\t\t\t\tsuperRuns++\n\t\t\t\t\t\tobserved = a() + this.b\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@effect override compute() {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\tobserved = (a() + this.b) * 2 // override without super\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst o = new Sub()\n\t\t\t\texpect(superRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(observed).toBe((1 + 2) * 2)\n\n\t\t\t\tsetA(3)\n\t\t\t\texpect(superRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(observed).toBe((3 + 2) * 2)\n\n\t\t\t\to.b = 5\n\t\t\t\texpect(superRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(3)\n\t\t\t\texpect(observed).toBe((3 + 5) * 2)\n\n\t\t\t\to.stopEffects()\n\t\t\t\tsetA(10)\n\t\t\t\to.b = 1\n\t\t\t\texpect(subRuns).toBe(3)\n\t\t\t})\n\t\t})\n\n\t\tit('works with nested effects', () => {\n\t\t\tlet outerRuns = 0\n\t\t\tlet innerRuns = 0\n\n\t\t\tclass MyEffects {\n\t\t\t\t@signal a = 0\n\t\t\t\t@signal b = 0\n\n\t\t\t\t@effect outer() {\n\t\t\t\t\touterRuns++\n\t\t\t\t\tthis.a\n\n\t\t\t\t\tcreateEffect(() => {\n\t\t\t\t\t\tinnerRuns++\n\t\t\t\t\t\tthis.b\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst e = new MyEffects()\n\n\t\t\texpect(outerRuns).toBe(1)\n\t\t\texpect(innerRuns).toBe(1)\n\n\t\t\tstartEffects(e) // should not duplicate effects (already started)\n\n\t\t\texpect(outerRuns).toBe(1)\n\t\t\texpect(innerRuns).toBe(1)\n\n\t\t\te.a = 1\n\t\t\texpect(outerRuns).toBe(2)\n\t\t\texpect(innerRuns).toBe(2) // inner effect runs because outer effect re-ran\n\n\t\t\te.b = 1\n\t\t\texpect(outerRuns).toBe(2)\n\t\t\texpect(innerRuns).toBe(3) // inner effect runs independently\n\n\t\t\tstopEffects(e)\n\n\t\t\texpect(outerRuns).toBe(2)\n\t\t\texpect(innerRuns).toBe(3)\n\n\t\t\tstartEffects(e)\n\n\t\t\texpect(outerRuns).toBe(3)\n\t\t\texpect(innerRuns).toBe(4) // inner effect runs because outer effect re-ran\n\n\t\t\te.b = 2\n\t\t\texpect(outerRuns).toBe(3)\n\t\t\texpect(innerRuns).toBe(5) // inner effect runs independently\n\t\t})\n\n\t\tdescribe('invalid usage', () => {\n\t\t\tit('throws on invalid field usage', () => {\n\t\t\t\texpect(() => {\n\t\t\t\t\tclass BadField {\n\t\t\t\t\t\t// @ts-expect-error invalid decorator usage on field\n\t\t\t\t\t\t@effect nope = () => 123\n\t\t\t\t\t}\n\t\t\t\t\tnew BadField()\n\t\t\t\t}).toThrow('@effect can only be used on methods or function-valued accessors')\n\t\t\t})\n\n\t\t\tit('throws on invalid getter usage', () => {\n\t\t\t\texpect(() => {\n\t\t\t\t\tclass BadGetter {\n\t\t\t\t\t\t@signal a = 1\n\t\t\t\t\t\t// @ts-expect-error invalid decorator usage on getter\n\t\t\t\t\t\t@effect get nope() {\n\t\t\t\t\t\t\treturn this.a\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\t\t\t\t\tnew BadGetter()\n\t\t\t\t}).toThrow('@effect can only be used on methods or function-valued accessors')\n\t\t\t})\n\n\t\t\tit('throws on invalid static usage', () => {\n\t\t\t\texpect(() => {\n\t\t\t\t\tclass BadStatic {\n\t\t\t\t\t\t@effect static nope() {}\n\t\t\t\t\t}\n\t\t\t\t\tBadStatic\n\t\t\t\t}).toThrow('@effect is not supported on static members.')\n\t\t\t})\n\n\t\t\tit('throws on invalid non-function value', () => {\n\t\t\t\texpect(() => {\n\t\t\t\t\tclass NonFunction {\n\t\t\t\t\t\t@signal a = 1\n\t\t\t\t\t\t// @ts-expect-error invalid decorator usage on non-function\n\t\t\t\t\t\t@effect accessor nope: number = 123\n\t\t\t\t\t}\n\t\t\t\t\tnew NonFunction()\n\t\t\t\t}).toThrow('@effect decorated member \"nope\" is not a function')\n\t\t\t})\n\n\t\t\tit('throws on duplicate members', () => {\n\t\t\t\tconst run = () => {\n\t\t\t\t\tclass SuperDuper {\n\t\t\t\t\t\t// @ts-expect-error duplicate member\n\t\t\t\t\t\t@effect dupe() {\n\t\t\t\t\t\t\tthis\n\t\t\t\t\t\t}\n\n\t\t\t\t\t\t// @ts-expect-error duplicate member\n\t\t\t\t\t\t@effect dupe() {\n\t\t\t\t\t\t\tthis\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tnew SuperDuper()\n\t\t\t\t}\n\n\t\t\t\t// When compiling with Babel, decorators currently throw an error when applied onto multiple members of the same name.\n\t\t\t\texpect(run).toThrow('Decorating two elements with the same name (dupe) is not supported yet')\n\n\t\t\t\t// When compiling with TypeScript, decorating duplicate members is allowed, and the last one wins.\n\t\t\t\t// expect(run).toThrow(\n\t\t\t\t// \t'@effect decorated member \"dupe\" has already been effectified. This can happen if there are duplicated class members.',\n\t\t\t\t// )\n\t\t\t})\n\t\t})\n\n\t\tdescribe('usage with custom elements', () => {\n\t\t\tit('@effect methods, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => {\n\t\t\t\tconst el = document.createElement('my-element4') as MyElement4\n\t\t\t\texpect(el.result).toBe(1 + 2)\n\t\t\t\texpect(el.runs).toBe(1) // already ran in constructor\n\n\t\t\t\ttestElementEffects(el)\n\t\t\t})\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/index.d.ts b/dist/decorators/index.d.ts index c215c43..5ec0702 100644 --- a/dist/decorators/index.d.ts +++ b/dist/decorators/index.d.ts @@ -1,4 +1,5 @@ export * from './component.js'; +export * from './effect.js'; export * from './memo.js'; export * from './reactive.js'; export * from './signal.js'; diff --git a/dist/decorators/index.d.ts.map b/dist/decorators/index.d.ts.map index e5fc158..3ca5cb7 100644 --- a/dist/decorators/index.d.ts.map +++ b/dist/decorators/index.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA"} \ No newline at end of file +{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/decorators/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAA;AAC9B,cAAc,aAAa,CAAA;AAC3B,cAAc,WAAW,CAAA;AACzB,cAAc,eAAe,CAAA;AAC7B,cAAc,aAAa,CAAA;AAC3B,cAAc,gBAAgB,CAAA"} \ No newline at end of file diff --git a/dist/decorators/index.js b/dist/decorators/index.js index 1d542b3..32c8862 100644 --- a/dist/decorators/index.js +++ b/dist/decorators/index.js @@ -1,4 +1,5 @@ export * from './component.js'; +export * from './effect.js'; export * from './memo.js'; export * from './reactive.js'; export * from './signal.js'; diff --git a/dist/decorators/index.js.map b/dist/decorators/index.js.map index c89ccc3..74cfba9 100644 --- a/dist/decorators/index.js.map +++ b/dist/decorators/index.js.map @@ -1 +1 @@ -{"version":3,"file":"index.js","names":[],"sources":["../../src/decorators/index.ts"],"sourcesContent":["export * from './component.js'\nexport * from './memo.js'\nexport * from './reactive.js'\nexport * from './signal.js'\nexport * from './untracked.js'\n"],"mappings":"AAAA,cAAc,gBAAgB;AAC9B,cAAc,WAAW;AACzB,cAAc,eAAe;AAC7B,cAAc,aAAa;AAC3B,cAAc,gBAAgB","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"index.js","names":[],"sources":["../../src/decorators/index.ts"],"sourcesContent":["export * from './component.js'\nexport * from './effect.js'\nexport * from './memo.js'\nexport * from './reactive.js'\nexport * from './signal.js'\nexport * from './untracked.js'\n"],"mappings":"AAAA,cAAc,gBAAgB;AAC9B,cAAc,aAAa;AAC3B,cAAc,WAAW;AACzB,cAAc,eAAe;AAC7B,cAAc,aAAa;AAC3B,cAAc,gBAAgB","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/memo.d.ts b/dist/decorators/memo.d.ts index 21812ca..52b63be 100644 --- a/dist/decorators/memo.d.ts +++ b/dist/decorators/memo.d.ts @@ -1,3 +1,4 @@ +import './metadata-shim.js'; /** * A decorator that make a signal property derived from a memoized computation * based on other signals. Effects depending on this property will re-run when @@ -8,36 +9,43 @@ * ```ts * import {reactive, signal, memo} from "classy-solid"; * - * @reactive * class Example { * @signal a = 1 * @signal b = 2 * - * // @memo can be used on a field, getter, or accessor. - * - * // Writable memo via field (requires function to have a parameter (arity > 0)) - * @memo sum = (v?: number) => this.a + this.b + * // @memo can be used on a getter, accessor, or method, with readonly and + * // writable variants: * * // Readonly memo via getter only + * @memo get sum1() { + * return this.a + this.b + * } + * + * // Writable memo via getter with setter * @memo get sum2() { * return this.a + this.b * } + * @memo set sum2(value: number) { + * // use an empty setter to enable writable (logic in here will be ignored if any) + * } * - * // Readonly memo via accessor (requires arrow function, not writable because no parameter (arity = 0)) + * // Readonly memo via auto accessor (requires arrow function, not writable because no parameter (arity = 0)) * @memo accessor sum3 = () => this.a + this.b * + * // Writable memo via auto accessor (requires arrow function with parameter, arity > 0) + * // Ignore the parameter, it only enables writable memo + * @memo accessor sum4 = (v?: number) => this.a + this.b + * * // Readonly memo via method - * @memo sum4() { + * @memo sum5() { * return this.a + this.b * } * - * // Writable memo via getter with setter - * @memo get sum5() { + * // Writable memo via method with parameter (arity > 0) + * @memo sum6(value?: number) { + * // Ignore the parameter, it only enables writable memo * return this.a + this.b * } - * @memo set sum5(value: number) { - * // empty setter makes it writable (logic in here will be ignored if any) - * } * * // The following variants are not supported yet as no runtime or TS support exists yet for the syntax. * @@ -65,29 +73,29 @@ * } * } * - * const ex = new Example(); + * const ex = new Example() * - * console.log(ex.sum(), ex.sum2, ex.sum3(), ex.sum4(), ex.sum5); // 3 3 3 3 3 + * console.log(ex.sum1, ex.sum2, ex.sum3(), ex.sum4(), ex.sum5(), ex.sum6()) // 3 3 3 3 3 3 * * createEffect(() => { - * console.log(ex.sum(), ex.sum2, ex.sum3(), ex.sum4(), ex.sum5); + * console.log(ex.sum1, ex.sum2, ex.sum3(), ex.sum4(), ex.sum5(), ex.sum6()) * }); * - * ex.a = 5; // Logs: 7 7 7 7 7 + * ex.a = 5 // Logs: 7 7 7 7 7 7 * * // This won't log anything since the computed memo values don't change (all still 7). * batch(() => { - * ex.a = 3; - * ex.b = 4; + * ex.a = 3 + * ex.b = 4 * }) * - * ex.sum(20); // Logs: 20 7 7 7 7 (only sum is updated) + * ex.sum2 = 20 // Logs: 20 7 7 7 7 7 (only sum2 is updated) * - * ex.sum5 = 15; // Logs: 20 7 7 7 15 (only sum5 is updated) + * ex.sum6(15) // Logs: 20 7 7 7 7 15 (only sum6 is updated) * - * ex.sum2 = 10; // Runtime error: Cannot set readonly property "sum2". + * ex.sum1 = 10 // Runtime error: Cannot set readonly property "sum1". * ``` */ -export declare function memo(_value: undefined | ((val?: any) => any) | (() => any) | ((val?: any) => void) | (() => void) | ClassAccessorDecoratorTarget any> | ClassAccessorDecoratorTarget any>, // today's auto-accessors, writable memo -context: ClassFieldDecoratorContext | ClassGetterDecoratorContext | ClassSetterDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext): void; +export declare function memo(value: ((val?: any) => any) | (() => any) | ((val?: any) => void) | (() => void) | ClassAccessorDecoratorTarget any> | ClassAccessorDecoratorTarget any>, // today's auto-accessors, writable memo +context: ClassGetterDecoratorContext | ClassSetterDecoratorContext | ClassAccessorDecoratorContext | ClassMethodDecoratorContext): void; //# sourceMappingURL=memo.d.ts.map \ No newline at end of file diff --git a/dist/decorators/memo.d.ts.map b/dist/decorators/memo.d.ts.map index e4fdd48..7558634 100644 --- a/dist/decorators/memo.d.ts.map +++ b/dist/decorators/memo.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"memo.d.ts","sourceRoot":"","sources":["../../src/decorators/memo.ts"],"names":[],"mappings":"AAUA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyFG;AACH,wBAAgB,IAAI,CACnB,MAAM,EACH,SAAS,GACT,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,GAAG,CAAC,GACpB,CAAC,MAAM,GAAG,CAAC,GACX,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,GACrB,CAAC,MAAM,IAAI,CAAC,GACZ,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,GAChD,4BAA4B,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,GAAG,CAAC,EAAE,wCAAwC;AACzG,OAAO,EACJ,0BAA0B,GAC1B,2BAA2B,GAC3B,2BAA2B,GAC3B,6BAA6B,GAC7B,2BAA2B,QA6D9B"} \ No newline at end of file +{"version":3,"file":"memo.d.ts","sourceRoot":"","sources":["../../src/decorators/memo.ts"],"names":[],"mappings":"AAEA,OAAO,oBAAoB,CAAA;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgGG;AACH,wBAAgB,IAAI,CACnB,KAAK,EACF,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,GAAG,CAAC,GACpB,CAAC,MAAM,GAAG,CAAC,GACX,CAAC,CAAC,GAAG,CAAC,EAAE,GAAG,KAAK,IAAI,CAAC,GACrB,CAAC,MAAM,IAAI,CAAC,GACZ,4BAA4B,CAAC,OAAO,EAAE,MAAM,GAAG,CAAC,GAChD,4BAA4B,CAAC,OAAO,EAAE,CAAC,GAAG,CAAC,EAAE,MAAM,KAAK,GAAG,CAAC,EAAE,wCAAwC;AACzG,OAAO,EACJ,2BAA2B,GAC3B,2BAA2B,GAC3B,6BAA6B,GAC7B,2BAA2B,QA+B9B"} \ No newline at end of file diff --git a/dist/decorators/memo.js b/dist/decorators/memo.js index e47e74f..3b716cf 100644 --- a/dist/decorators/memo.js +++ b/dist/decorators/memo.js @@ -1,4 +1,5 @@ -import { finalizeMemos, getMemberStat, getSignalsAndMemos, isPriorSignalOrMemoDefined, memoifyIfNeeded, sortSignalsMemosInMetadata } from '../_state.js'; +import { finalizeMembersIfLast, getMemberStat, getMembers, memoifyIfNeeded } from '../_state.js'; +import './metadata-shim.js'; /** * A decorator that make a signal property derived from a memoized computation @@ -10,36 +11,43 @@ import { finalizeMemos, getMemberStat, getSignalsAndMemos, isPriorSignalOrMemoDe * ```ts * import {reactive, signal, memo} from "classy-solid"; * - * @reactive * class Example { * @signal a = 1 * @signal b = 2 * - * // @memo can be used on a field, getter, or accessor. - * - * // Writable memo via field (requires function to have a parameter (arity > 0)) - * @memo sum = (v?: number) => this.a + this.b + * // @memo can be used on a getter, accessor, or method, with readonly and + * // writable variants: * * // Readonly memo via getter only + * @memo get sum1() { + * return this.a + this.b + * } + * + * // Writable memo via getter with setter * @memo get sum2() { * return this.a + this.b * } + * @memo set sum2(value: number) { + * // use an empty setter to enable writable (logic in here will be ignored if any) + * } * - * // Readonly memo via accessor (requires arrow function, not writable because no parameter (arity = 0)) + * // Readonly memo via auto accessor (requires arrow function, not writable because no parameter (arity = 0)) * @memo accessor sum3 = () => this.a + this.b * + * // Writable memo via auto accessor (requires arrow function with parameter, arity > 0) + * // Ignore the parameter, it only enables writable memo + * @memo accessor sum4 = (v?: number) => this.a + this.b + * * // Readonly memo via method - * @memo sum4() { + * @memo sum5() { * return this.a + this.b * } * - * // Writable memo via getter with setter - * @memo get sum5() { + * // Writable memo via method with parameter (arity > 0) + * @memo sum6(value?: number) { + * // Ignore the parameter, it only enables writable memo * return this.a + this.b * } - * @memo set sum5(value: number) { - * // empty setter makes it writable (logic in here will be ignored if any) - * } * * // The following variants are not supported yet as no runtime or TS support exists yet for the syntax. * @@ -67,30 +75,30 @@ import { finalizeMemos, getMemberStat, getSignalsAndMemos, isPriorSignalOrMemoDe * } * } * - * const ex = new Example(); + * const ex = new Example() * - * console.log(ex.sum(), ex.sum2, ex.sum3(), ex.sum4(), ex.sum5); // 3 3 3 3 3 + * console.log(ex.sum1, ex.sum2, ex.sum3(), ex.sum4(), ex.sum5(), ex.sum6()) // 3 3 3 3 3 3 * * createEffect(() => { - * console.log(ex.sum(), ex.sum2, ex.sum3(), ex.sum4(), ex.sum5); + * console.log(ex.sum1, ex.sum2, ex.sum3(), ex.sum4(), ex.sum5(), ex.sum6()) * }); * - * ex.a = 5; // Logs: 7 7 7 7 7 + * ex.a = 5 // Logs: 7 7 7 7 7 7 * * // This won't log anything since the computed memo values don't change (all still 7). * batch(() => { - * ex.a = 3; - * ex.b = 4; + * ex.a = 3 + * ex.b = 4 * }) * - * ex.sum(20); // Logs: 20 7 7 7 7 (only sum is updated) + * ex.sum2 = 20 // Logs: 20 7 7 7 7 7 (only sum2 is updated) * - * ex.sum5 = 15; // Logs: 20 7 7 7 15 (only sum5 is updated) + * ex.sum6(15) // Logs: 20 7 7 7 7 15 (only sum6 is updated) * - * ex.sum2 = 10; // Runtime error: Cannot set readonly property "sum2". + * ex.sum1 = 10 // Runtime error: Cannot set readonly property "sum1". * ``` */ -export function memo(_value, +export function memo(value, // today's auto-accessors, writable memo context) { if (context.static) throw new Error('@memo is not supported on static fields yet.'); @@ -99,53 +107,24 @@ context) { name } = context; const metadata = context.metadata; - const signalsAndMemos = getSignalsAndMemos(metadata); - if (kind === 'field') { - const stat = getMemberStat(name, 'memo-field', signalsAndMemos); - context.addInitializer(function () { - sortSignalsMemosInMetadata(metadata); - memoifyIfNeeded(this, name, stat); - - // If we skipped memoifying prior memo members (accessor and method - // memos) because of prior signal-fields, memo-fields, or - // memo-auto-accessors, finalize those memos now. - finalizeMemos(this, stat, signalsAndMemos); - }); - } else if (kind === 'accessor') { - const stat = getMemberStat(name, 'memo-auto-accessor', signalsAndMemos); - context.addInitializer(function () { - sortSignalsMemosInMetadata(metadata); - memoifyIfNeeded(this, name, stat, true); - - // If we skipped memoifying prior memo members (accessor and method - // memos) because of prior signal-fields, memo-fields, or - // memo-auto-accessors, finalize those memos now. - finalizeMemos(this, stat, signalsAndMemos); - }); - } else if (kind === 'method') { - const stat = getMemberStat(name, 'memo-method', signalsAndMemos); - context.addInitializer(function () { - sortSignalsMemosInMetadata(metadata); + const signalsAndMemos = getMembers(metadata); - // If any signal-fields, memo-fields, or memo-auto-accessors are - // defined on the class (thus sorted before this memo method), skip - // memoifying now because we need those to be initialized first, - // then we'll memoify after that. - if (isPriorSignalOrMemoDefined(this, name, signalsAndMemos)) return; - memoifyIfNeeded(this, name, stat); - }); - } else if (kind === 'getter' || kind === 'setter') { - const stat = getMemberStat(name, 'memo-accessor', signalsAndMemos); - context.addInitializer(function () { - sortSignalsMemosInMetadata(metadata); + // Apply finalization logic to all except setters (setters are finalized + // together with their getters). + // By skipping setters we also avoid double-counting the getter+setter pair + // in the finalizeMembersIfLast logic. + if (kind === 'setter') return; - // If any signal-fields, memo-fields, or memo-auto-accessors are - // defined on the class (thus sorted before this memo method), skip - // memoifying now because we need those to be initialized first, - // then we'll memoify after that. - if (isPriorSignalOrMemoDefined(this, name, signalsAndMemos)) return; - memoifyIfNeeded(this, name, stat); - }); - } + // @ts-expect-error skip type checking in case of invalid kind in plain JS + if (kind === 'field') throw new Error('@memo is not supported on class fields.'); + const memberType = kind === 'accessor' ? 'memo-auto-accessor' : kind === 'method' ? 'memo-method' : 'memo-accessor'; + const stat = getMemberStat(name, memberType, signalsAndMemos); + stat.finalize = function () { + memoifyIfNeeded(this, name, stat); + }; + context.addInitializer(function () { + finalizeMembersIfLast(this, signalsAndMemos); + }); + if (kind === 'method' || kind === 'getter') stat.value = value;else if (kind === 'accessor') stat.value = value.get; } //# sourceMappingURL=memo.js.map \ No newline at end of file diff --git a/dist/decorators/memo.js.map b/dist/decorators/memo.js.map index a48137d..20c4abf 100644 --- a/dist/decorators/memo.js.map +++ b/dist/decorators/memo.js.map @@ -1 +1 @@ -{"version":3,"file":"memo.js","names":["finalizeMemos","getMemberStat","getSignalsAndMemos","isPriorSignalOrMemoDefined","memoifyIfNeeded","sortSignalsMemosInMetadata","memo","_value","context","static","Error","kind","name","metadata","signalsAndMemos","stat","addInitializer"],"sources":["../../src/decorators/memo.ts"],"sourcesContent":["import type {SignalMetadata} from './types.js'\nimport {\n\tfinalizeMemos,\n\tgetMemberStat,\n\tgetSignalsAndMemos,\n\tisPriorSignalOrMemoDefined,\n\tmemoifyIfNeeded,\n\tsortSignalsMemosInMetadata,\n} from '../_state.js'\n\n/**\n * A decorator that make a signal property derived from a memoized computation\n * based on other signals. Effects depending on this property will re-run when\n * the computed value changes, but not if the computed value stays the same even\n * if the dependencies changed.\n *\n * @example\n * ```ts\n * import {reactive, signal, memo} from \"classy-solid\";\n *\n * @reactive\n * class Example {\n * @signal a = 1\n * @signal b = 2\n *\n * // @memo can be used on a field, getter, or accessor.\n *\n * // Writable memo via field (requires function to have a parameter (arity > 0))\n * @memo sum = (v?: number) => this.a + this.b\n *\n * // Readonly memo via getter only\n * @memo get sum2() {\n * return this.a + this.b\n * }\n *\n * // Readonly memo via accessor (requires arrow function, not writable because no parameter (arity = 0))\n * @memo accessor sum3 = () => this.a + this.b\n *\n * // Readonly memo via method\n * @memo sum4() {\n * return this.a + this.b\n * }\n *\n * // Writable memo via getter with setter\n * @memo get sum5() {\n * return this.a + this.b\n * }\n * @memo set sum5(value: number) {\n * // empty setter makes it writable (logic in here will be ignored if any)\n * }\n *\n * // The following variants are not supported yet as no runtime or TS support exists yet for the syntax.\n *\n * // Writable memo via accessor, alternative long-hand syntax (not yet released, no runtime or TS support yet)\n * @memo accessor sum6 { get; set } = () => this.a + this.b\n *\n * // Readonly memo via accessor with only getter (not released yet, no runtime or TS support yet)\n * @memo accessor sum7 { get; } = () => this.a + this.b\n *\n * // Readonly memo via accessor with only getter, alternative syntax (not released yet, no runtime or TS support yet)\n * @memo accessor sum8 {\n * get() {\n * return this.a + this.b\n * }\n * }\n *\n * // Readonly memo via accessor with only getter, alternative syntax (not released yet, no runtime or TS support yet)\n * @memo accessor sum8 {\n * get() {\n * return this.a + this.b\n * }\n * set(_v: number) {\n * // empty setter makes it writable (logic in here will be ignored if any)\n * }\n * }\n * }\n *\n * const ex = new Example();\n *\n * console.log(ex.sum(), ex.sum2, ex.sum3(), ex.sum4(), ex.sum5); // 3 3 3 3 3\n *\n * createEffect(() => {\n * console.log(ex.sum(), ex.sum2, ex.sum3(), ex.sum4(), ex.sum5);\n * });\n *\n * ex.a = 5; // Logs: 7 7 7 7 7\n *\n * // This won't log anything since the computed memo values don't change (all still 7).\n * batch(() => {\n * ex.a = 3;\n * ex.b = 4;\n * })\n *\n * ex.sum(20); // Logs: 20 7 7 7 7 (only sum is updated)\n *\n * ex.sum5 = 15; // Logs: 20 7 7 7 15 (only sum5 is updated)\n *\n * ex.sum2 = 10; // Runtime error: Cannot set readonly property \"sum2\".\n * ```\n */\nexport function memo(\n\t_value:\n\t\t| undefined\n\t\t| ((val?: any) => any) // writable memo via field or method\n\t\t| (() => any) // readonly memo via field or method\n\t\t| ((val?: any) => void) // memo setter\n\t\t| (() => void) // memo getter\n\t\t| ClassAccessorDecoratorTarget any> // today's auto-accessors, readonly memo\n\t\t| ClassAccessorDecoratorTarget any>, // today's auto-accessors, writable memo\n\tcontext:\n\t\t| ClassFieldDecoratorContext\n\t\t| ClassGetterDecoratorContext\n\t\t| ClassSetterDecoratorContext\n\t\t| ClassAccessorDecoratorContext\n\t\t| ClassMethodDecoratorContext,\n) {\n\tif (context.static) throw new Error('@memo is not supported on static fields yet.')\n\n\tconst {kind, name} = context\n\tconst metadata = context.metadata as SignalMetadata\n\tconst signalsAndMemos = getSignalsAndMemos(metadata)\n\n\tif (kind === 'field') {\n\t\tconst stat = getMemberStat(name, 'memo-field', signalsAndMemos)\n\n\t\tcontext.addInitializer(function () {\n\t\t\tsortSignalsMemosInMetadata(metadata)\n\t\t\tmemoifyIfNeeded(this as object, name, stat)\n\n\t\t\t// If we skipped memoifying prior memo members (accessor and method\n\t\t\t// memos) because of prior signal-fields, memo-fields, or\n\t\t\t// memo-auto-accessors, finalize those memos now.\n\t\t\tfinalizeMemos(this as object, stat, signalsAndMemos)\n\t\t})\n\t} else if (kind === 'accessor') {\n\t\tconst stat = getMemberStat(name, 'memo-auto-accessor', signalsAndMemos)\n\n\t\tcontext.addInitializer(function () {\n\t\t\tsortSignalsMemosInMetadata(metadata)\n\t\t\tmemoifyIfNeeded(this as object, name, stat, true)\n\n\t\t\t// If we skipped memoifying prior memo members (accessor and method\n\t\t\t// memos) because of prior signal-fields, memo-fields, or\n\t\t\t// memo-auto-accessors, finalize those memos now.\n\t\t\tfinalizeMemos(this as object, stat, signalsAndMemos)\n\t\t})\n\t} else if (kind === 'method') {\n\t\tconst stat = getMemberStat(name, 'memo-method', signalsAndMemos)\n\n\t\tcontext.addInitializer(function () {\n\t\t\tsortSignalsMemosInMetadata(metadata)\n\n\t\t\t// If any signal-fields, memo-fields, or memo-auto-accessors are\n\t\t\t// defined on the class (thus sorted before this memo method), skip\n\t\t\t// memoifying now because we need those to be initialized first,\n\t\t\t// then we'll memoify after that.\n\t\t\tif (isPriorSignalOrMemoDefined(this as object, name, signalsAndMemos)) return\n\n\t\t\tmemoifyIfNeeded(this as object, name, stat)\n\t\t})\n\t} else if (kind === 'getter' || kind === 'setter') {\n\t\tconst stat = getMemberStat(name, 'memo-accessor', signalsAndMemos)\n\n\t\tcontext.addInitializer(function () {\n\t\t\tsortSignalsMemosInMetadata(metadata)\n\n\t\t\t// If any signal-fields, memo-fields, or memo-auto-accessors are\n\t\t\t// defined on the class (thus sorted before this memo method), skip\n\t\t\t// memoifying now because we need those to be initialized first,\n\t\t\t// then we'll memoify after that.\n\t\t\tif (isPriorSignalOrMemoDefined(this as object, name, signalsAndMemos)) return\n\n\t\t\tmemoifyIfNeeded(this as object, name, stat)\n\t\t})\n\t}\n}\n"],"mappings":"AACA,SACCA,aAAa,EACbC,aAAa,EACbC,kBAAkB,EAClBC,0BAA0B,EAC1BC,eAAe,EACfC,0BAA0B,QACpB,cAAc;;AAErB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,IAAIA,CACnBC,MAO+D;AAAE;AACjEC,OAK8B,EAC7B;EACD,IAAIA,OAAO,CAACC,MAAM,EAAE,MAAM,IAAIC,KAAK,CAAC,8CAA8C,CAAC;EAEnF,MAAM;IAACC,IAAI;IAAEC;EAAI,CAAC,GAAGJ,OAAO;EAC5B,MAAMK,QAAQ,GAAGL,OAAO,CAACK,QAA0B;EACnD,MAAMC,eAAe,GAAGZ,kBAAkB,CAACW,QAAQ,CAAC;EAEpD,IAAIF,IAAI,KAAK,OAAO,EAAE;IACrB,MAAMI,IAAI,GAAGd,aAAa,CAACW,IAAI,EAAE,YAAY,EAAEE,eAAe,CAAC;IAE/DN,OAAO,CAACQ,cAAc,CAAC,YAAY;MAClCX,0BAA0B,CAACQ,QAAQ,CAAC;MACpCT,eAAe,CAAC,IAAI,EAAYQ,IAAI,EAAEG,IAAI,CAAC;;MAE3C;MACA;MACA;MACAf,aAAa,CAAC,IAAI,EAAYe,IAAI,EAAED,eAAe,CAAC;IACrD,CAAC,CAAC;EACH,CAAC,MAAM,IAAIH,IAAI,KAAK,UAAU,EAAE;IAC/B,MAAMI,IAAI,GAAGd,aAAa,CAACW,IAAI,EAAE,oBAAoB,EAAEE,eAAe,CAAC;IAEvEN,OAAO,CAACQ,cAAc,CAAC,YAAY;MAClCX,0BAA0B,CAACQ,QAAQ,CAAC;MACpCT,eAAe,CAAC,IAAI,EAAYQ,IAAI,EAAEG,IAAI,EAAE,IAAI,CAAC;;MAEjD;MACA;MACA;MACAf,aAAa,CAAC,IAAI,EAAYe,IAAI,EAAED,eAAe,CAAC;IACrD,CAAC,CAAC;EACH,CAAC,MAAM,IAAIH,IAAI,KAAK,QAAQ,EAAE;IAC7B,MAAMI,IAAI,GAAGd,aAAa,CAACW,IAAI,EAAE,aAAa,EAAEE,eAAe,CAAC;IAEhEN,OAAO,CAACQ,cAAc,CAAC,YAAY;MAClCX,0BAA0B,CAACQ,QAAQ,CAAC;;MAEpC;MACA;MACA;MACA;MACA,IAAIV,0BAA0B,CAAC,IAAI,EAAYS,IAAI,EAAEE,eAAe,CAAC,EAAE;MAEvEV,eAAe,CAAC,IAAI,EAAYQ,IAAI,EAAEG,IAAI,CAAC;IAC5C,CAAC,CAAC;EACH,CAAC,MAAM,IAAIJ,IAAI,KAAK,QAAQ,IAAIA,IAAI,KAAK,QAAQ,EAAE;IAClD,MAAMI,IAAI,GAAGd,aAAa,CAACW,IAAI,EAAE,eAAe,EAAEE,eAAe,CAAC;IAElEN,OAAO,CAACQ,cAAc,CAAC,YAAY;MAClCX,0BAA0B,CAACQ,QAAQ,CAAC;;MAEpC;MACA;MACA;MACA;MACA,IAAIV,0BAA0B,CAAC,IAAI,EAAYS,IAAI,EAAEE,eAAe,CAAC,EAAE;MAEvEV,eAAe,CAAC,IAAI,EAAYQ,IAAI,EAAEG,IAAI,CAAC;IAC5C,CAAC,CAAC;EACH;AACD","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"memo.js","names":["finalizeMembersIfLast","getMemberStat","getMembers","memoifyIfNeeded","memo","value","context","static","Error","kind","name","metadata","signalsAndMemos","memberType","stat","finalize","addInitializer","get"],"sources":["../../src/decorators/memo.ts"],"sourcesContent":["import type {AnyObject, ClassySolidMetadata} from './types.js'\nimport {finalizeMembersIfLast, getMemberStat, getMembers, memoifyIfNeeded} from '../_state.js'\nimport './metadata-shim.js'\n\n/**\n * A decorator that make a signal property derived from a memoized computation\n * based on other signals. Effects depending on this property will re-run when\n * the computed value changes, but not if the computed value stays the same even\n * if the dependencies changed.\n *\n * @example\n * ```ts\n * import {reactive, signal, memo} from \"classy-solid\";\n *\n * class Example {\n * @signal a = 1\n * @signal b = 2\n *\n * // @memo can be used on a getter, accessor, or method, with readonly and\n * // writable variants:\n *\n * // Readonly memo via getter only\n * @memo get sum1() {\n * return this.a + this.b\n * }\n *\n * // Writable memo via getter with setter\n * @memo get sum2() {\n * return this.a + this.b\n * }\n * @memo set sum2(value: number) {\n * // use an empty setter to enable writable (logic in here will be ignored if any)\n * }\n *\n * // Readonly memo via auto accessor (requires arrow function, not writable because no parameter (arity = 0))\n * @memo accessor sum3 = () => this.a + this.b\n *\n * // Writable memo via auto accessor (requires arrow function with parameter, arity > 0)\n * // Ignore the parameter, it only enables writable memo\n * @memo accessor sum4 = (v?: number) => this.a + this.b\n *\n * // Readonly memo via method\n * @memo sum5() {\n * return this.a + this.b\n * }\n *\n * // Writable memo via method with parameter (arity > 0)\n * @memo sum6(value?: number) {\n * // Ignore the parameter, it only enables writable memo\n * return this.a + this.b\n * }\n *\n * // The following variants are not supported yet as no runtime or TS support exists yet for the syntax.\n *\n * // Writable memo via accessor, alternative long-hand syntax (not yet released, no runtime or TS support yet)\n * @memo accessor sum6 { get; set } = () => this.a + this.b\n *\n * // Readonly memo via accessor with only getter (not released yet, no runtime or TS support yet)\n * @memo accessor sum7 { get; } = () => this.a + this.b\n *\n * // Readonly memo via accessor with only getter, alternative syntax (not released yet, no runtime or TS support yet)\n * @memo accessor sum8 {\n * get() {\n * return this.a + this.b\n * }\n * }\n *\n * // Readonly memo via accessor with only getter, alternative syntax (not released yet, no runtime or TS support yet)\n * @memo accessor sum8 {\n * get() {\n * return this.a + this.b\n * }\n * set(_v: number) {\n * // empty setter makes it writable (logic in here will be ignored if any)\n * }\n * }\n * }\n *\n * const ex = new Example()\n *\n * console.log(ex.sum1, ex.sum2, ex.sum3(), ex.sum4(), ex.sum5(), ex.sum6()) // 3 3 3 3 3 3\n *\n * createEffect(() => {\n * console.log(ex.sum1, ex.sum2, ex.sum3(), ex.sum4(), ex.sum5(), ex.sum6())\n * });\n *\n * ex.a = 5 // Logs: 7 7 7 7 7 7\n *\n * // This won't log anything since the computed memo values don't change (all still 7).\n * batch(() => {\n * ex.a = 3\n * ex.b = 4\n * })\n *\n * ex.sum2 = 20 // Logs: 20 7 7 7 7 7 (only sum2 is updated)\n *\n * ex.sum6(15) // Logs: 20 7 7 7 7 15 (only sum6 is updated)\n *\n * ex.sum1 = 10 // Runtime error: Cannot set readonly property \"sum1\".\n * ```\n */\nexport function memo(\n\tvalue:\n\t\t| ((val?: any) => any) // writable memo via field or method\n\t\t| (() => any) // readonly memo via field or method\n\t\t| ((val?: any) => void) // memo setter\n\t\t| (() => void) // memo getter\n\t\t| ClassAccessorDecoratorTarget any> // today's auto-accessors, readonly memo\n\t\t| ClassAccessorDecoratorTarget any>, // today's auto-accessors, writable memo\n\tcontext:\n\t\t| ClassGetterDecoratorContext\n\t\t| ClassSetterDecoratorContext\n\t\t| ClassAccessorDecoratorContext\n\t\t| ClassMethodDecoratorContext,\n) {\n\tif (context.static) throw new Error('@memo is not supported on static fields yet.')\n\n\tconst {kind, name} = context\n\tconst metadata = context.metadata as ClassySolidMetadata\n\tconst signalsAndMemos = getMembers(metadata)\n\n\t// Apply finalization logic to all except setters (setters are finalized\n\t// together with their getters).\n\t// By skipping setters we also avoid double-counting the getter+setter pair\n\t// in the finalizeMembersIfLast logic.\n\tif (kind === 'setter') return\n\n\t// @ts-expect-error skip type checking in case of invalid kind in plain JS\n\tif (kind === 'field') throw new Error('@memo is not supported on class fields.')\n\n\tconst memberType = kind === 'accessor' ? 'memo-auto-accessor' : kind === 'method' ? 'memo-method' : 'memo-accessor'\n\n\tconst stat = getMemberStat(name, memberType, signalsAndMemos)\n\n\tstat.finalize = function (this: unknown) {\n\t\tmemoifyIfNeeded(this as AnyObject, name, stat)\n\t}\n\n\tcontext.addInitializer(function () {\n\t\tfinalizeMembersIfLast(this as AnyObject, signalsAndMemos)\n\t})\n\n\tif (kind === 'method' || kind === 'getter') stat.value = value\n\telse if (kind === 'accessor') stat.value = (value as ClassAccessorDecoratorTarget void>).get\n}\n"],"mappings":"AACA,SAAQA,qBAAqB,EAAEC,aAAa,EAAEC,UAAU,EAAEC,eAAe,QAAO,cAAc;AAC9F,OAAO,oBAAoB;;AAE3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,IAAIA,CACnBC,KAM+D;AAAE;AACjEC,OAI8B,EAC7B;EACD,IAAIA,OAAO,CAACC,MAAM,EAAE,MAAM,IAAIC,KAAK,CAAC,8CAA8C,CAAC;EAEnF,MAAM;IAACC,IAAI;IAAEC;EAAI,CAAC,GAAGJ,OAAO;EAC5B,MAAMK,QAAQ,GAAGL,OAAO,CAACK,QAA+B;EACxD,MAAMC,eAAe,GAAGV,UAAU,CAACS,QAAQ,CAAC;;EAE5C;EACA;EACA;EACA;EACA,IAAIF,IAAI,KAAK,QAAQ,EAAE;;EAEvB;EACA,IAAIA,IAAI,KAAK,OAAO,EAAE,MAAM,IAAID,KAAK,CAAC,yCAAyC,CAAC;EAEhF,MAAMK,UAAU,GAAGJ,IAAI,KAAK,UAAU,GAAG,oBAAoB,GAAGA,IAAI,KAAK,QAAQ,GAAG,aAAa,GAAG,eAAe;EAEnH,MAAMK,IAAI,GAAGb,aAAa,CAACS,IAAI,EAAEG,UAAU,EAAED,eAAe,CAAC;EAE7DE,IAAI,CAACC,QAAQ,GAAG,YAAyB;IACxCZ,eAAe,CAAC,IAAI,EAAeO,IAAI,EAAEI,IAAI,CAAC;EAC/C,CAAC;EAEDR,OAAO,CAACU,cAAc,CAAC,YAAY;IAClChB,qBAAqB,CAAC,IAAI,EAAeY,eAAe,CAAC;EAC1D,CAAC,CAAC;EAEF,IAAIH,IAAI,KAAK,QAAQ,IAAIA,IAAI,KAAK,QAAQ,EAAEK,IAAI,CAACT,KAAK,GAAGA,KAAK,MACzD,IAAII,IAAI,KAAK,UAAU,EAAEK,IAAI,CAACT,KAAK,GAAIA,KAAK,CAAuDY,GAAG;AAC5G","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/memo.test.js b/dist/decorators/memo.test.js index 0689eb2..9303fed 100644 --- a/dist/decorators/memo.test.js +++ b/dist/decorators/memo.test.js @@ -3,103 +3,23 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } -import { createEffect, batch } from 'solid-js'; +import { createEffect, batch, createSignal } from 'solid-js'; import { signal } from './signal.js'; import { memo } from './memo.js'; +import { effect } from './effect.js'; describe('classy-solid', () => { - describe('@memo', () => { - it('creates a readonly memo via field', () => { - let _init_a, _init_extra_a, _init_b, _init_extra_b, _init_sum, _init_extra_sum; - class Example { - static { - [_init_a, _init_extra_a, _init_b, _init_extra_b, _init_sum, _init_extra_sum] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 0, "sum"]]).e; - } - constructor() { - _init_extra_sum(this); - } - a = _init_a(this, 1); - b = (_init_extra_a(this), _init_b(this, 2)); - sum = (_init_extra_b(this), _init_sum(this, () => this.a + this.b)); - } - const ex = new Example(); - let count = 0; - let lastSum = 0; - createEffect(() => { - lastSum = ex.sum(); - count++; - }); - expect(ex.sum()).toBe(3); - expect(count).toBe(1); - ex.a = 5; - expect(ex.sum()).toBe(7); - expect(lastSum).toBe(7); - expect(count).toBe(2); - - // This should not trigger the effect since the computed value doesn't change (still 7) - batch(() => { - ex.a = 3; - ex.b = 4; - }); - expect(ex.sum()).toBe(7); - expect(lastSum).toBe(7); - expect(count).toBe(2); // count should still be 2, not 3 - - // @ts-expect-error Readonly memo cannot be set - should throw - expect(() => ex.sum(20)).toThrow(); - }); - it('creates a writable memo via field', () => { - let _init_a2, _init_extra_a2, _init_b2, _init_extra_b2, _init_sum2, _init_extra_sum2; - class Example { - static { - [_init_a2, _init_extra_a2, _init_b2, _init_extra_b2, _init_sum2, _init_extra_sum2] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 0, "sum"]]).e; - } - constructor() { - _init_extra_sum2(this); - } - a = _init_a2(this, 1); - b = (_init_extra_a2(this), _init_b2(this, 2)); - sum = (_init_extra_b2(this), _init_sum2(this, _val => this.a + this.b)); - } - const ex = new Example(); - let count = 0; - let lastSum = 0; - createEffect(() => { - lastSum = ex.sum(); - count++; - }); - expect(ex.sum()).toBe(3); - expect(count).toBe(1); - ex.a = 5; - expect(ex.sum()).toBe(7); - expect(lastSum).toBe(7); - expect(count).toBe(2); - - // This should not trigger the effect since the computed value doesn't change (still 7) - batch(() => { - ex.a = 3; - ex.b = 4; - }); - expect(ex.sum()).toBe(7); - expect(lastSum).toBe(7); - expect(count).toBe(2); // count should still be 2, not 3 - - // Writable memo can be set directly - ex.sum(20); - expect(ex.sum()).toBe(20); - expect(lastSum).toBe(20); - expect(count).toBe(3); - }); + describe('@memo decorator', () => { it('creates a readonly memo via getter', () => { - let _initProto, _init_a3, _init_extra_a3, _init_b3, _init_extra_b3; + let _initProto, _init_a, _init_extra_a, _init_b, _init_extra_b; class Example { static { - [_init_a3, _init_extra_a3, _init_b3, _init_extra_b3, _initProto] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 3, "sum2"]]).e; + [_init_a, _init_extra_a, _init_b, _init_extra_b, _initProto] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 3, "sum2"]]).e; } constructor() { - _init_extra_b3(this); + _init_extra_b(this); } - a = (_initProto(this), _init_a3(this, 1)); - b = (_init_extra_a3(this), _init_b3(this, 2)); + a = (_initProto(this), _init_a(this, 1)); + b = (_init_extra_a(this), _init_b(this, 2)); get sum2() { return this.a + this.b; } @@ -134,16 +54,16 @@ describe('classy-solid', () => { }).toThrow(); }); it('creates a writable memo via getter+setter', () => { - let _initProto2, _init_a4, _init_extra_a4, _init_b4, _init_extra_b4; + let _initProto2, _init_a2, _init_extra_a2, _init_b2, _init_extra_b2; class Example { static { - [_init_a4, _init_extra_a4, _init_b4, _init_extra_b4, _initProto2] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 3, "sum2"], [memo, 4, "sum2"]]).e; + [_init_a2, _init_extra_a2, _init_b2, _init_extra_b2, _initProto2] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 3, "sum2"], [memo, 4, "sum2"]]).e; } constructor() { - _init_extra_b4(this); + _init_extra_b2(this); } - a = (_initProto2(this), _init_a4(this, 1)); - b = (_init_extra_a4(this), _init_b4(this, 2)); + a = (_initProto2(this), _init_a2(this, 1)); + b = (_init_extra_a2(this), _init_b2(this, 2)); get sum2() { return this.a + this.b; } @@ -179,17 +99,17 @@ describe('classy-solid', () => { expect(count).toBe(3); }); it('creates a readonly memo via accessor function value', () => { - let _init_a5, _init_extra_a5, _init_b5, _init_extra_b5, _init_sum3, _init_extra_sum3; + let _init_a3, _init_extra_a3, _init_b3, _init_extra_b3, _init_sum, _init_extra_sum; class Example { static { - [_init_sum3, _init_extra_sum3, _init_a5, _init_extra_a5, _init_b5, _init_extra_b5] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 1, "sum3"]]).e; + [_init_sum, _init_extra_sum, _init_a3, _init_extra_a3, _init_b3, _init_extra_b3] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 1, "sum3"]]).e; } constructor() { - _init_extra_sum3(this); + _init_extra_sum(this); } - a = _init_a5(this, 1); - b = (_init_extra_a5(this), _init_b5(this, 2)); - #A = (_init_extra_b5(this), _init_sum3(this, () => this.a + this.b)); + a = _init_a3(this, 1); + b = (_init_extra_a3(this), _init_b3(this, 2)); + #A = (_init_extra_b3(this), _init_sum(this, () => this.a + this.b)); get sum3() { return this.#A; } @@ -198,18 +118,18 @@ describe('classy-solid', () => { } } const ex = new Example(); - let count = 0; + let runs = 0; let lastSum = 0; createEffect(() => { lastSum = ex.sum3(); - count++; + runs++; }); expect(ex.sum3()).toBe(3); - expect(count).toBe(1); + expect(runs).toBe(1); ex.a = 5; expect(ex.sum3()).toBe(7); expect(lastSum).toBe(7); - expect(count).toBe(2); + expect(runs).toBe(2); // This should not trigger the effect since the computed value doesn't change (still 7) batch(() => { @@ -218,23 +138,23 @@ describe('classy-solid', () => { }); expect(ex.sum3()).toBe(7); expect(lastSum).toBe(7); - expect(count).toBe(2); // count should still be 2, not 3 + expect(runs).toBe(2); // count should still be 2, not 3 // @ts-expect-error Readonly memo cannot be set - should throw expect(() => ex.sum3(20)).toThrow(); }); it('creates a writable memo via accessor function value', () => { - let _init_a6, _init_extra_a6, _init_b6, _init_extra_b6, _init_sum4, _init_extra_sum4; + let _init_a4, _init_extra_a4, _init_b4, _init_extra_b4, _init_sum2, _init_extra_sum2; class Example { static { - [_init_sum4, _init_extra_sum4, _init_a6, _init_extra_a6, _init_b6, _init_extra_b6] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 1, "sum3"]]).e; + [_init_sum2, _init_extra_sum2, _init_a4, _init_extra_a4, _init_b4, _init_extra_b4] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 1, "sum3"]]).e; } constructor() { - _init_extra_sum4(this); + _init_extra_sum2(this); } - a = _init_a6(this, 1); - b = (_init_extra_a6(this), _init_b6(this, 2)); - #A = (_init_extra_b6(this), _init_sum4(this, _val => this.a + this.b)); + a = _init_a4(this, 1); + b = (_init_extra_a4(this), _init_b4(this, 2)); + #A = (_init_extra_b4(this), _init_sum2(this, _val => this.a + this.b)); get sum3() { return this.#A; } @@ -272,16 +192,16 @@ describe('classy-solid', () => { expect(count).toBe(3); }); it('creates a readonly memo via method', () => { - let _initProto3, _init_a7, _init_extra_a7, _init_b7, _init_extra_b7; + let _initProto3, _init_a5, _init_extra_a5, _init_b5, _init_extra_b5; class Example { static { - [_init_a7, _init_extra_a7, _init_b7, _init_extra_b7, _initProto3] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 2, "sum4"]]).e; + [_init_a5, _init_extra_a5, _init_b5, _init_extra_b5, _initProto3] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 2, "sum4"]]).e; } constructor() { - _init_extra_b7(this); + _init_extra_b5(this); } - a = (_initProto3(this), _init_a7(this, 1)); - b = (_init_extra_a7(this), _init_b7(this, 2)); + a = (_initProto3(this), _init_a5(this, 1)); + b = (_init_extra_a5(this), _init_b5(this, 2)); sum4() { return this.a + this.b; } @@ -316,16 +236,16 @@ describe('classy-solid', () => { }).toThrow(); }); it('creates a writable memo via method', () => { - let _initProto4, _init_a8, _init_extra_a8, _init_b8, _init_extra_b8; + let _initProto4, _init_a6, _init_extra_a6, _init_b6, _init_extra_b6; class Example { static { - [_init_a8, _init_extra_a8, _init_b8, _init_extra_b8, _initProto4] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 2, "sum4"]]).e; + [_init_a6, _init_extra_a6, _init_b6, _init_extra_b6, _initProto4] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 2, "sum4"]]).e; } constructor() { - _init_extra_b8(this); + _init_extra_b6(this); } - a = (_initProto4(this), _init_a8(this, 1)); - b = (_init_extra_a8(this), _init_b8(this, 2)); + a = (_initProto4(this), _init_a6(this, 1)); + b = (_init_extra_a6(this), _init_b6(this, 2)); sum4(_val) { return this.a + this.b; } @@ -394,20 +314,22 @@ describe('classy-solid', () => { expect(calc.computeCount).toBe(2); }); it('works with multiple memo properties', () => { - let _initProto6, _init_value, _init_extra_value, _init_double, _init_extra_double, _init_quadruple, _init_extra_quadruple; + let _initProto6, _init_value, _init_extra_value, _init_quadruple, _init_extra_quadruple; class MultiMemo { static { - [_init_quadruple, _init_extra_quadruple, _init_value, _init_extra_value, _init_double, _init_extra_double, _initProto6] = _applyDecs(this, [], [[signal, 0, "value"], [memo, 0, "double"], [memo, 3, "triple"], [memo, 1, "quadruple"]]).e; + [_init_quadruple, _init_extra_quadruple, _init_value, _init_extra_value, _initProto6] = _applyDecs(this, [], [[signal, 0, "value"], [memo, 2, "double"], [memo, 3, "triple"], [memo, 1, "quadruple"]]).e; } constructor() { _init_extra_quadruple(this); } value = (_initProto6(this), _init_value(this, 10)); - double = (_init_extra_value(this), _init_double(this, () => this.value * 2)); + double() { + return this.value * 2; + } get triple() { return this.value * 3; } - #A = (_init_extra_double(this), _init_quadruple(this, () => this.value * 4)); + #A = (_init_extra_value(this), _init_quadruple(this, () => this.value * 4)); get quadruple() { return this.#A; } @@ -476,54 +398,17 @@ describe('classy-solid', () => { expect(cm.cubed).toBe(27); expect(count).toBe(2); }); - it('correctly handles writable field memo overriding explicit value', () => { - let _init_a9, _init_extra_a9, _init_b9, _init_extra_b9, _init_sum5, _init_extra_sum5; - class WritableOverride { - static { - [_init_a9, _init_extra_a9, _init_b9, _init_extra_b9, _init_sum5, _init_extra_sum5] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 0, "sum"]]).e; - } - constructor() { - _init_extra_sum5(this); - } - a = _init_a9(this, 5); - b = (_init_extra_a9(this), _init_b9(this, 10)); - sum = (_init_extra_b9(this), _init_sum5(this, _val => this.a + this.b)); - } - const wo = new WritableOverride(); - let count = 0; - let lastValue = 0; - createEffect(() => { - lastValue = wo.sum(); - count++; - }); - expect(wo.sum()).toBe(15); - expect(count).toBe(1); - expect(lastValue).toBe(15); - - // Override with direct value - wo.sum(100); - expect(wo.sum()).toBe(100); - expect(count).toBe(2); - expect(lastValue).toBe(100); - - // Changing dependencies should still work after override - wo.a = 20; - // The memo should now compute based on signals again - expect(wo.sum()).toBe(30); // 20 + 10 - expect(count).toBe(3); - expect(lastValue).toBe(30); - }); it('correctly handles writable getter+setter memo overriding explicit value', () => { - let _initProto8, _init_a0, _init_extra_a0, _init_b0, _init_extra_b0; + let _initProto8, _init_a7, _init_extra_a7, _init_b7, _init_extra_b7; class WritableOverride { static { - [_init_a0, _init_extra_a0, _init_b0, _init_extra_b0, _initProto8] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 3, "sum"], [memo, 4, "sum"]]).e; + [_init_a7, _init_extra_a7, _init_b7, _init_extra_b7, _initProto8] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 3, "sum"], [memo, 4, "sum"]]).e; } constructor() { - _init_extra_b0(this); + _init_extra_b7(this); } - a = (_initProto8(this), _init_a0(this, 5)); - b = (_init_extra_a0(this), _init_b0(this, 10)); + a = (_initProto8(this), _init_a7(this, 5)); + b = (_init_extra_a7(this), _init_b7(this, 10)); get sum() { return this.a + this.b; } @@ -554,17 +439,17 @@ describe('classy-solid', () => { expect(lastValue).toBe(30); }); it('correctly handles writable accessor memo overriding explicit value', () => { - let _init_a1, _init_extra_a1, _init_b1, _init_extra_b1, _init_sum6, _init_extra_sum6; + let _init_a8, _init_extra_a8, _init_b8, _init_extra_b8, _init_sum3, _init_extra_sum3; class WritableOverride { static { - [_init_sum6, _init_extra_sum6, _init_a1, _init_extra_a1, _init_b1, _init_extra_b1] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 1, "sum"]]).e; + [_init_sum3, _init_extra_sum3, _init_a8, _init_extra_a8, _init_b8, _init_extra_b8] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 1, "sum"]]).e; } constructor() { - _init_extra_sum6(this); + _init_extra_sum3(this); } - a = _init_a1(this, 5); - b = (_init_extra_a1(this), _init_b1(this, 10)); - #A = (_init_extra_b1(this), _init_sum6(this, _val => this.a + this.b)); + a = _init_a8(this, 5); + b = (_init_extra_a8(this), _init_b8(this, 10)); + #A = (_init_extra_b8(this), _init_sum3(this, _val => this.a + this.b)); get sum() { return this.#A; } @@ -597,16 +482,16 @@ describe('classy-solid', () => { expect(lastValue).toBe(30); }); it('correctly handles writable method memo overriding explicit value', () => { - let _initProto9, _init_a10, _init_extra_a10, _init_b10, _init_extra_b10; + let _initProto9, _init_a9, _init_extra_a9, _init_b9, _init_extra_b9; class WritableOverride { static { - [_init_a10, _init_extra_a10, _init_b10, _init_extra_b10, _initProto9] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 2, "sum"]]).e; + [_init_a9, _init_extra_a9, _init_b9, _init_extra_b9, _initProto9] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 2, "sum"]]).e; } constructor() { - _init_extra_b10(this); + _init_extra_b9(this); } - a = (_initProto9(this), _init_a10(this, 5)); - b = (_init_extra_a10(this), _init_b10(this, 10)); + a = (_initProto9(this), _init_a9(this, 5)); + b = (_init_extra_a9(this), _init_b9(this, 10)); sum(_val) { return this.a + this.b; } @@ -662,6 +547,527 @@ describe('classy-solid', () => { expect(val).toBe(42); expect(count).toBe(1); }); + describe('subclass memo overriding/extending', () => { + it('supports subclass memo extending base memo (getter)', () => { + let _initProto1, _init_a0, _init_extra_a0, _initProto10; + class Base { + static { + [_init_a0, _init_extra_a0, _initProto1] = _applyDecs(this, [], [[signal, 0, "a"], [memo, 3, "baseVal"]]).e; + } + constructor() { + _init_extra_a0(this); + } + a = (_initProto1(this), _init_a0(this, 1)); + get baseVal() { + return this.a + 1; + } + } + class Sub extends Base { + static { + [_initProto10] = _applyDecs(this, [], [[memo, 3, "baseVal"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _initProto10(this); + } + get baseVal() { + return super.baseVal + 1; // extend + } + } + const s = new Sub(); + let runs = 0; + let last = 0; + createEffect(() => { + runs++; + last = s.baseVal; + }); + expect(last).toBe(1 + 1 + 1); + expect(runs).toBe(1); + s.a = 5; + expect(last).toBe(5 + 1 + 1); + expect(runs).toBe(2); + }); + it('supports subclass memo overriding base memo (getter no super)', () => { + let _initProto11, _init_a1, _init_extra_a1, _initProto12; + class Base { + static { + [_init_a1, _init_extra_a1, _initProto11] = _applyDecs(this, [], [[signal, 0, "a"], [memo, 3, "val"]]).e; + } + constructor() { + _init_extra_a1(this); + } + a = (_initProto11(this), _init_a1(this, 1)); + get val() { + return this.a + 1; + } + } + class Sub extends Base { + static { + [_initProto12] = _applyDecs(this, [], [[memo, 3, "val"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _initProto12(this); + } + get val() { + return this.a * 2; // override + } + } + const s = new Sub(); + let runs = 0; + let last = 0; + createEffect(() => { + runs++; + last = s.val; + }); + expect(last).toBe(1 * 2); + expect(runs).toBe(1); + s.a = 5; + expect(last).toBe(5 * 2); + expect(runs).toBe(2); + }); + it('supports getter override with no super', () => { + let _initProto13, _initProto14; + const [a, setA] = createSignal(10); + let baseRuns = 0; + let subRuns = 0; + class Base { + static { + [_initProto13] = _applyDecs(this, [], [[memo, 3, "val"]]).e; + } + constructor() { + _initProto13(this); + } + get val() { + baseRuns++; + return a() + 1; + } + } + class Sub extends Base { + static { + [_initProto14] = _applyDecs(this, [], [[memo, 3, "val"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _initProto14(this); + } + get val() { + subRuns++; + return a() + 10; + } + } + const o = new Sub(); + let effectRuns = 0; + let effectVal = 0; + createEffect(() => { + effectRuns++; + effectVal = o.val; + }); + expect(effectVal).toBe(10 + 10); + expect(baseRuns).toBe(0); + expect(subRuns).toBe(1); + expect(effectRuns).toBe(1); + setA(20); + expect(effectVal).toBe(20 + 10); + expect(baseRuns).toBe(0); + expect(subRuns).toBe(2); + expect(effectRuns).toBe(2); + }); + it('supports multi-level getter extension with super', () => { + let _initProto15, _initProto16, _initProto17; + const [a, setA] = createSignal(10); + let baseRuns = 0; + let midRuns = 0; + let subRuns = 0; + class Base { + static { + [_initProto15] = _applyDecs(this, [], [[memo, 3, "val"]]).e; + } + constructor() { + _initProto15(this); + } + get val() { + baseRuns++; + return a() + 1; + } + } + class Mid extends Base { + static { + [_initProto16] = _applyDecs(this, [], [[memo, 3, "val"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _initProto16(this); + } + get val() { + midRuns++; + return super.val + 10; + } + } + class Sub extends Mid { + static { + [_initProto17] = _applyDecs(this, [], [[memo, 3, "val"]], 0, void 0, Mid).e; + } + constructor(...args) { + super(...args); + _initProto17(this); + } + get val() { + subRuns++; + return super.val + 100; + } + } + const o = new Sub(); + let effectRuns = 0; + let effectVal = 0; + createEffect(() => { + effectRuns++; + effectVal = o.val; + }); + expect(effectVal).toBe(10 + 1 + 10 + 100); + expect(baseRuns).toBe(1); + expect(midRuns).toBe(1); + expect(subRuns).toBe(1); + expect(effectRuns).toBe(1); + setA(20); + expect(effectVal).toBe(20 + 1 + 10 + 100); + expect(baseRuns).toBe(2); + expect(midRuns).toBe(2); + expect(subRuns).toBe(2); + expect(effectRuns).toBe(2); + }); + it('supports subclass memo method extension with super', () => { + let _initProto18, _init_a10, _init_extra_a10, _initProto19; + let baseRuns = 0; + class BaseM { + static { + [_init_a10, _init_extra_a10, _initProto18] = _applyDecs(this, [], [[signal, 0, "a"], [memo, 2, "val"]]).e; + } + constructor() { + _init_extra_a10(this); + } + a = (_initProto18(this), _init_a10(this, 1)); + val() { + baseRuns++; + return this.a + 1; + } + } + let subRuns = 0; + class SubM extends BaseM { + static { + [_initProto19] = _applyDecs(this, [], [[memo, 2, "val"]], 0, void 0, BaseM).e; + } + constructor(...args) { + super(...args); + _initProto19(this); + } + val() { + subRuns++; + return super.val() + 2; + } + } + const s = new SubM(); + let effectRuns = 0; + let last = 0; + createEffect(() => { + effectRuns++; + last = s.val(); + }); + expect(last).toBe(1 + 1 + 2); + expect(baseRuns).toBe(1); + expect(subRuns).toBe(1); + expect(effectRuns).toBe(1); + s.a = 5; + expect(last).toBe(5 + 1 + 2); + expect(baseRuns).toBe(2); + expect(subRuns).toBe(2); + expect(effectRuns).toBe(2); + }); + it('supports subclass memo method override with no super', () => { + let _initProto20, _init_a11, _init_extra_a11, _initProto21; + let baseRuns = 0; + let subRuns = 0; + class BaseM { + static { + [_init_a11, _init_extra_a11, _initProto20] = _applyDecs(this, [], [[signal, 0, "a"], [memo, 2, "val"]]).e; + } + constructor() { + _init_extra_a11(this); + } + a = (_initProto20(this), _init_a11(this, 1)); + val() { + baseRuns++; + return this.a + 1; + } + } + class SubM extends BaseM { + static { + [_initProto21] = _applyDecs(this, [], [[memo, 2, "val"]], 0, void 0, BaseM).e; + } + constructor(...args) { + super(...args); + _initProto21(this); + } + val() { + subRuns++; + return this.a + 2; + } + } + const s = new SubM(); + let effectRuns = 0; + let last = 0; + createEffect(() => { + effectRuns++; + last = s.val(); + }); + expect(last).toBe(1 + 2); + expect(baseRuns).toBe(0); + expect(subRuns).toBe(1); + expect(effectRuns).toBe(1); + s.a = 5; + expect(last).toBe(5 + 2); + expect(baseRuns).toBe(0); + expect(subRuns).toBe(2); + expect(effectRuns).toBe(2); + }); + it('supports subclass memo auto accessor extension with super', () => { + let _init_a12, _init_extra_a12, _init_val, _init_extra_val, _init_val2, _init_extra_val2; + let baseRuns = 0; + let subRuns = 0; + class BaseFO { + static { + [_init_val, _init_extra_val, _init_a12, _init_extra_a12] = _applyDecs(this, [], [[signal, 0, "a"], [memo, 1, "val"]]).e; + } + constructor() { + _init_extra_val(this); + } + a = _init_a12(this, 1); + #A = (_init_extra_a12(this), _init_val(this, () => { + baseRuns++; + return this.a + 1; + })); + get val() { + return this.#A; + } + set val(v) { + this.#A = v; + } + } + class SubFO extends BaseFO { + static { + [_init_val2, _init_extra_val2] = _applyDecs(this, [], [[memo, 1, "val"]], 0, void 0, BaseFO).e; + } + constructor(...args) { + super(...args); + _init_extra_val2(this); + } + #A = _init_val2(this, () => { + subRuns++; + return super.val() * 3; + }); + get val() { + return this.#A; + } + set val(v) { + this.#A = v; + } + } + const s = new SubFO(); + let effectRuns = 0; + let last = 0; + createEffect(() => { + effectRuns++; + last = s.val(); + }); + expect(last).toBe((1 + 1) * 3); + expect(baseRuns).toBe(1); + expect(subRuns).toBe(1); + expect(effectRuns).toBe(1); + s.a = 4; + expect(last).toBe((4 + 1) * 3); + expect(baseRuns).toBe(2); + expect(subRuns).toBe(2); + expect(effectRuns).toBe(2); + }); + it('supports subclass memo auto accessor override with no super', () => { + let _init_a13, _init_extra_a13, _init_val3, _init_extra_val3, _init_val4, _init_extra_val4; + let baseRuns = 0; + let subRuns = 0; + class BaseFO { + static { + [_init_val3, _init_extra_val3, _init_a13, _init_extra_a13] = _applyDecs(this, [], [[signal, 0, "a"], [memo, 1, "val"]]).e; + } + constructor() { + _init_extra_val3(this); + } + a = _init_a13(this, 1); + #A = (_init_extra_a13(this), _init_val3(this, () => { + baseRuns++; + return this.a + 1; + })); + get val() { + return this.#A; + } + set val(v) { + this.#A = v; + } + } + class SubFO extends BaseFO { + static { + [_init_val4, _init_extra_val4] = _applyDecs(this, [], [[memo, 1, "val"]], 0, void 0, BaseFO).e; + } + constructor(...args) { + super(...args); + _init_extra_val4(this); + } + #A = _init_val4(this, () => { + subRuns++; + return this.a * 3; + }); + get val() { + return this.#A; + } + set val(v) { + this.#A = v; + } + } + const s = new SubFO(); + let effectRuns = 0; + let last = 0; + createEffect(() => { + effectRuns++; + last = s.val(); + }); + expect(last).toBe(1 * 3); + expect(baseRuns).toBe(0); + expect(subRuns).toBe(1); + expect(effectRuns).toBe(1); + s.a = 4; + expect(last).toBe(4 * 3); + expect(baseRuns).toBe(0); + expect(subRuns).toBe(2); + expect(effectRuns).toBe(2); + }); + }); + describe('invalid usage', () => { + it('throws on non-function value', () => { + let _init_foo, _init_extra_foo; + class Base { + static { + [_init_foo, _init_extra_foo] = _applyDecs(this, [], [[memo, 1, "foo"]]).e; + } + constructor() { + _init_extra_foo(this); + } + // @ts-expect-error non-function value + #A = _init_foo(this, 1); + get foo() { + return this.#A; + } + set foo(v) { + this.#A = v; + } + } + expect(() => new Base()).toThrow('memo value for "foo" is not a function: 1'); + }); + it('throws on @memo used on class field', () => { + const [a] = createSignal(10); + expect(() => { + let _init_a14, _init_extra_a14; + class InvalidMemo { + static { + [_init_a14, _init_extra_a14] = _applyDecs(this, [], [[memo, 0, "a"]]).e; + } + constructor() { + _init_extra_a14(this); + } + // @ts-expect-error @memo not usable on fields + a = _init_a14(this, () => a()); + } + new InvalidMemo(); + }).toThrow('@memo is not supported on class fields.'); + }); + it('throws on duplicate members', () => { + const run = () => { + let _initProto22; + class SuperDuper { + static { + [_initProto22] = _applyDecs(this, [], [[memo, 3, "dupe"], [memo, 3, "dupe"]]).e; + } + constructor() { + _initProto22(this); + } + // @ts-expect-error duplicate member + get dupe() { + return 2; + } + // @ts-expect-error duplicate member + get dupe() { + return 3; + } + } + new SuperDuper(); + }; + + // When compiling with Babel, decorators currently throw an error when applied onto multiple members of the same name. + expect(run).toThrow('Decorating two elements with the same name (get dupe) is not supported yet'); + + // When compiling with TypeScript, decorating duplicate members is allowed, and the last one wins. + // expect(run).toThrow( + // '@memo decorated member "dupe" has already been memoified. This can happen if there are duplicated class members.', + // ) + + // TODO ^ update Babel to latest in @lume/cli, see if decorators on duplicate members work in classy-solid + }); + it('throws due to TDZ when accessing private fields defined after regular fields', () => { + let _initProto23, _init_bar, _init_extra_bar, _initProto24, _init_bar2, _init_extra_bar2; + class Bar { + static { + [_init_bar, _init_extra_bar, _initProto23] = _applyDecs(this, [], [[signal, 0, "bar"], [signal, 3, "baz"], [signal, 4, "baz"], [effect, 2, "logBar"]]).e; + } + bar = (_initProto23(this), _init_bar(this, 456)); + #baz = (_init_extra_bar(this), 789); + get baz() { + return this.#baz; + } + set baz(v) { + this.#baz = v; + } + + // This throws because #baz is used before its initialization + // The ordering is: + // 1. bar field initialized + // 2. bar field runs finalizers because it is last in the ordering of extra initializers (so #baz is not initialized yet) + // 3. During the logBar finalizer (executed in the bar extra initializer), the baz getter is accessed, which accesses #baz before it is initialized + logBar() { + console.log('this.baz:', this.baz); + } + } + expect(() => new Bar()).toThrow('Cannot read private member #baz from an object whose class did not declare it'); + + // To work around the problem, place private fields before regular fields: + class Bar2 { + static { + [_init_bar2, _init_extra_bar2, _initProto24] = _applyDecs(this, [], [[signal, 0, "bar"], [signal, 3, "baz"], [signal, 4, "baz"], [effect, 2, "logBar"]]).e; + } + constructor() { + _init_extra_bar2(this); + } + #baz = (_initProto24(this), 789); + bar = _init_bar2(this, 456); + get baz() { + return this.#baz; + } + set baz(v) { + this.#baz = v; + } + logBar() { + console.log('this.baz:', this.baz); + } + } + expect(() => new Bar2()).not.toThrow(); + }); + }); }); }); //# sourceMappingURL=memo.test.js.map \ No newline at end of file diff --git a/dist/decorators/memo.test.js.map b/dist/decorators/memo.test.js.map index ff50863..38e6fe7 100644 --- a/dist/decorators/memo.test.js.map +++ b/dist/decorators/memo.test.js.map @@ -1 +1 @@ -{"version":3,"file":"memo.test.js","names":["createEffect","batch","signal","memo","describe","it","_init_a","_init_extra_a","_init_b","_init_extra_b","_init_sum","_init_extra_sum","Example","_applyDecs","e","constructor","a","b","sum","ex","count","lastSum","expect","toBe","toThrow","_init_a2","_init_extra_a2","_init_b2","_init_extra_b2","_init_sum2","_init_extra_sum2","_val","_initProto","_init_a3","_init_extra_a3","_init_b3","_init_extra_b3","sum2","_initProto2","_init_a4","_init_extra_a4","_init_b4","_init_extra_b4","_init_a5","_init_extra_a5","_init_b5","_init_extra_b5","_init_sum3","_init_extra_sum3","A","sum3","v","_init_a6","_init_extra_a6","_init_b6","_init_extra_b6","_init_sum4","_init_extra_sum4","_initProto3","_init_a7","_init_extra_a7","_init_b7","_init_extra_b7","sum4","_initProto4","_init_a8","_init_extra_a8","_init_b8","_init_extra_b8","_initProto5","_init_x","_init_extra_x","_init_y","_init_extra_y","Calculator","computeCount","x","y","result","calc","_initProto6","_init_value","_init_extra_value","_init_double","_init_extra_double","_init_quadruple","_init_extra_quadruple","MultiMemo","value","double","triple","quadruple","mm","doubleCount","tripleCount","quadCount","_initProto7","_init_base","_init_extra_base","ChainedMemo","base","squared","cubed","cm","_init_a9","_init_extra_a9","_init_b9","_init_extra_b9","_init_sum5","_init_extra_sum5","WritableOverride","wo","lastValue","_initProto8","_init_a0","_init_extra_a0","_init_b0","_init_extra_b0","_init_a1","_init_extra_a1","_init_b1","_init_extra_b1","_init_sum6","_init_extra_sum6","_initProto9","_init_a10","_init_extra_a10","_init_b10","_init_extra_b10","_initProto0","ConstantMemo","constant","val"],"sources":["../../src/decorators/memo.test.ts"],"sourcesContent":["import {createEffect, batch} from 'solid-js'\nimport {signal} from './signal.js'\nimport {memo} from './memo.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('@memo', () => {\n\t\tit('creates a readonly memo via field', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo sum = () => this.a + this.b\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// @ts-expect-error Readonly memo cannot be set - should throw\n\t\t\texpect(() => ex.sum(20)).toThrow()\n\t\t})\n\n\t\tit('creates a writable memo via field', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo sum = (_val?: number) => this.a + this.b\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Writable memo can be set directly\n\t\t\tex.sum(20)\n\t\t\texpect(ex.sum()).toBe(20)\n\t\t\texpect(lastSum).toBe(20)\n\t\t\texpect(count).toBe(3)\n\t\t})\n\n\t\tit('creates a readonly memo via getter', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo get sum2() {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum2\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum2).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum2).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum2).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Readonly memo cannot be set - should throw in strict mode\n\t\t\texpect(() => {\n\t\t\t\t// @ts-expect-error - intentionally setting readonly property\n\t\t\t\tex.sum2 = 20\n\t\t\t}).toThrow()\n\t\t})\n\n\t\tit('creates a writable memo via getter+setter', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo get sum2() {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t\t@memo set sum2(_val: number) {}\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum2\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum2).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum2).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum2).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Writable memo can be set directly\n\t\t\tex.sum2 = 20\n\t\t\texpect(ex.sum2).toBe(20)\n\t\t\texpect(lastSum).toBe(20)\n\t\t\texpect(count).toBe(3)\n\t\t})\n\n\t\tit('creates a readonly memo via accessor function value', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo accessor sum3 = () => this.a + this.b\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum3()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum3()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum3()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum3()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// @ts-expect-error Readonly memo cannot be set - should throw\n\t\t\texpect(() => ex.sum3(20)).toThrow()\n\t\t})\n\n\t\tit('creates a writable memo via accessor function value', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo accessor sum3 = (_val?: number) => this.a + this.b\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum3()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum3()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum3()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum3()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Writable memo can be set directly\n\t\t\tex.sum3(20)\n\t\t\texpect(ex.sum3()).toBe(20)\n\t\t\texpect(lastSum).toBe(20)\n\t\t\texpect(count).toBe(3)\n\t\t})\n\n\t\tit('creates a readonly memo via method', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo sum4() {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum4()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum4()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum4()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum4()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Readonly memo cannot be set - should throw\n\t\t\texpect(() => {\n\t\t\t\t// @ts-expect-error - intentionally setting readonly memo\n\t\t\t\tex.sum4(20)\n\t\t\t}).toThrow()\n\t\t})\n\n\t\tit('creates a writable memo via method', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo sum4(_val?: number) {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum4()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum4()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum4()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum4()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Writable memo can be set directly\n\t\t\tex.sum4(20)\n\t\t\texpect(ex.sum4()).toBe(20)\n\t\t\texpect(lastSum).toBe(20)\n\t\t\texpect(count).toBe(3)\n\t\t})\n\n\t\tit('memoizes complex computations and only re-runs when dependencies change', () => {\n\t\t\tclass Calculator {\n\t\t\t\tcomputeCount = 0\n\t\t\t\t@signal x = 10\n\t\t\t\t@signal y = 5\n\n\t\t\t\t@memo get result() {\n\t\t\t\t\tthis.computeCount++\n\t\t\t\t\treturn this.x * 2 + this.y\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst calc = new Calculator()\n\n\t\t\texpect(calc.result).toBe(25)\n\t\t\texpect(calc.computeCount).toBe(1)\n\n\t\t\t// Reading again should not re-compute\n\t\t\texpect(calc.result).toBe(25)\n\t\t\texpect(calc.computeCount).toBe(1)\n\n\t\t\t// Changing a dependency should trigger recomputation\n\t\t\tcalc.x = 20\n\t\t\texpect(calc.result).toBe(45)\n\t\t\texpect(calc.computeCount).toBe(2)\n\n\t\t\t// Reading again should not re-compute\n\t\t\texpect(calc.result).toBe(45)\n\t\t\texpect(calc.computeCount).toBe(2)\n\t\t})\n\n\t\tit('works with multiple memo properties', () => {\n\t\t\tclass MultiMemo {\n\t\t\t\t@signal value = 10\n\n\t\t\t\t@memo double = () => this.value * 2\n\t\t\t\t@memo get triple() {\n\t\t\t\t\treturn this.value * 3\n\t\t\t\t}\n\t\t\t\t@memo accessor quadruple = () => this.value * 4\n\t\t\t}\n\n\t\t\tconst mm = new MultiMemo()\n\t\t\tlet doubleCount = 0\n\t\t\tlet tripleCount = 0\n\t\t\tlet quadCount = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tmm.double()\n\t\t\t\tdoubleCount++\n\t\t\t})\n\t\t\tcreateEffect(() => {\n\t\t\t\tmm.triple\n\t\t\t\ttripleCount++\n\t\t\t})\n\t\t\tcreateEffect(() => {\n\t\t\t\tmm.quadruple()\n\t\t\t\tquadCount++\n\t\t\t})\n\n\t\t\texpect(mm.double()).toBe(20)\n\t\t\texpect(mm.triple).toBe(30)\n\t\t\texpect(mm.quadruple()).toBe(40)\n\t\t\texpect(doubleCount).toBe(1)\n\t\t\texpect(tripleCount).toBe(1)\n\t\t\texpect(quadCount).toBe(1)\n\n\t\t\tmm.value = 5\n\t\t\texpect(mm.double()).toBe(10)\n\t\t\texpect(mm.triple).toBe(15)\n\t\t\texpect(mm.quadruple()).toBe(20)\n\t\t\texpect(doubleCount).toBe(2)\n\t\t\texpect(tripleCount).toBe(2)\n\t\t\texpect(quadCount).toBe(2)\n\t\t})\n\n\t\tit('handles memo depending on other memos', () => {\n\t\t\tclass ChainedMemo {\n\t\t\t\t@signal base = 2\n\n\t\t\t\t@memo get squared() {\n\t\t\t\t\treturn this.base * this.base\n\t\t\t\t}\n\n\t\t\t\t@memo get cubed() {\n\t\t\t\t\treturn this.squared * this.base\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst cm = new ChainedMemo()\n\t\t\tlet count = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tcm.cubed\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(cm.squared).toBe(4)\n\t\t\texpect(cm.cubed).toBe(8)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tcm.base = 3\n\t\t\texpect(cm.squared).toBe(9)\n\t\t\texpect(cm.cubed).toBe(27)\n\t\t\texpect(count).toBe(2)\n\t\t})\n\n\t\tit('correctly handles writable field memo overriding explicit value', () => {\n\t\t\tclass WritableOverride {\n\t\t\t\t@signal a = 5\n\t\t\t\t@signal b = 10\n\n\t\t\t\t@memo sum = (_val?: number) => this.a + this.b\n\t\t\t}\n\n\t\t\tconst wo = new WritableOverride()\n\t\t\tlet count = 0\n\t\t\tlet lastValue = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastValue = wo.sum()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(wo.sum()).toBe(15)\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(lastValue).toBe(15)\n\n\t\t\t// Override with direct value\n\t\t\two.sum(100)\n\t\t\texpect(wo.sum()).toBe(100)\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(lastValue).toBe(100)\n\n\t\t\t// Changing dependencies should still work after override\n\t\t\two.a = 20\n\t\t\t// The memo should now compute based on signals again\n\t\t\texpect(wo.sum()).toBe(30) // 20 + 10\n\t\t\texpect(count).toBe(3)\n\t\t\texpect(lastValue).toBe(30)\n\t\t})\n\n\t\tit('correctly handles writable getter+setter memo overriding explicit value', () => {\n\t\t\tclass WritableOverride {\n\t\t\t\t@signal a = 5\n\t\t\t\t@signal b = 10\n\n\t\t\t\t@memo get sum() {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t\t@memo set sum(_val: number) {}\n\t\t\t}\n\n\t\t\tconst wo = new WritableOverride()\n\t\t\tlet count = 0\n\t\t\tlet lastValue = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastValue = wo.sum\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(wo.sum).toBe(15)\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(lastValue).toBe(15)\n\n\t\t\t// Override with direct value\n\t\t\two.sum = 100\n\t\t\texpect(wo.sum).toBe(100)\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(lastValue).toBe(100)\n\n\t\t\t// Changing dependencies should still work after override\n\t\t\two.a = 20\n\t\t\t// The memo should now compute based on signals again\n\t\t\texpect(wo.sum).toBe(30) // 20 + 10\n\t\t\texpect(count).toBe(3)\n\t\t\texpect(lastValue).toBe(30)\n\t\t})\n\n\t\tit('correctly handles writable accessor memo overriding explicit value', () => {\n\t\t\tclass WritableOverride {\n\t\t\t\t@signal a = 5\n\t\t\t\t@signal b = 10\n\n\t\t\t\t@memo accessor sum = (_val?: number) => this.a + this.b\n\t\t\t}\n\n\t\t\tconst wo = new WritableOverride()\n\t\t\tlet count = 0\n\t\t\tlet lastValue = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastValue = wo.sum()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(wo.sum()).toBe(15)\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(lastValue).toBe(15)\n\n\t\t\t// Override with direct value\n\t\t\two.sum(100)\n\t\t\texpect(wo.sum()).toBe(100)\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(lastValue).toBe(100)\n\n\t\t\t// Changing dependencies should still work after override\n\t\t\two.a = 20\n\t\t\t// The memo should now compute based on signals again\n\t\t\texpect(wo.sum()).toBe(30) // 20 + 10\n\t\t\texpect(count).toBe(3)\n\t\t\texpect(lastValue).toBe(30)\n\t\t})\n\n\t\tit('correctly handles writable method memo overriding explicit value', () => {\n\t\t\tclass WritableOverride {\n\t\t\t\t@signal a = 5\n\t\t\t\t@signal b = 10\n\n\t\t\t\t@memo sum(_val?: number) {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst wo = new WritableOverride()\n\t\t\tlet count = 0\n\t\t\tlet lastValue = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastValue = wo.sum()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(wo.sum()).toBe(15)\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(lastValue).toBe(15)\n\n\t\t\t// Override with direct value\n\t\t\two.sum(100)\n\t\t\texpect(wo.sum()).toBe(100)\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(lastValue).toBe(100)\n\n\t\t\t// Changing dependencies should still work after override\n\t\t\two.a = 20\n\t\t\t// The memo should now compute based on signals again\n\t\t\texpect(wo.sum()).toBe(30) // 20 + 10\n\t\t\texpect(count).toBe(3)\n\t\t\texpect(lastValue).toBe(30)\n\t\t})\n\n\t\tit('handles memo with no dependencies', () => {\n\t\t\tclass ConstantMemo {\n\t\t\t\t@memo get constant() {\n\t\t\t\t\treturn 42\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst cm = new ConstantMemo()\n\t\t\tlet count = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tcm.constant\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(cm.constant).toBe(42)\n\t\t\texpect(count).toBe(1)\n\n\t\t\t// Reading again should not trigger effect\n\t\t\tconst val = cm.constant\n\t\t\texpect(val).toBe(42)\n\t\t\texpect(count).toBe(1)\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"memo.test.js","names":["createEffect","batch","createSignal","signal","memo","effect","describe","it","_initProto","_init_a","_init_extra_a","_init_b","_init_extra_b","Example","_applyDecs","e","constructor","a","b","sum2","ex","count","lastSum","expect","toBe","toThrow","_initProto2","_init_a2","_init_extra_a2","_init_b2","_init_extra_b2","_val","_init_a3","_init_extra_a3","_init_b3","_init_extra_b3","_init_sum","_init_extra_sum","A","sum3","v","runs","_init_a4","_init_extra_a4","_init_b4","_init_extra_b4","_init_sum2","_init_extra_sum2","_initProto3","_init_a5","_init_extra_a5","_init_b5","_init_extra_b5","sum4","_initProto4","_init_a6","_init_extra_a6","_init_b6","_init_extra_b6","_initProto5","_init_x","_init_extra_x","_init_y","_init_extra_y","Calculator","computeCount","x","y","result","calc","_initProto6","_init_value","_init_extra_value","_init_quadruple","_init_extra_quadruple","MultiMemo","value","double","triple","quadruple","mm","doubleCount","tripleCount","quadCount","_initProto7","_init_base","_init_extra_base","ChainedMemo","base","squared","cubed","cm","_initProto8","_init_a7","_init_extra_a7","_init_b7","_init_extra_b7","WritableOverride","sum","wo","lastValue","_init_a8","_init_extra_a8","_init_b8","_init_extra_b8","_init_sum3","_init_extra_sum3","_initProto9","_init_a9","_init_extra_a9","_init_b9","_init_extra_b9","_initProto0","ConstantMemo","constant","val","_initProto1","_init_a0","_init_extra_a0","_initProto10","Base","baseVal","Sub","args","s","last","_initProto11","_init_a1","_init_extra_a1","_initProto12","_initProto13","_initProto14","setA","baseRuns","subRuns","o","effectRuns","effectVal","_initProto15","_initProto16","_initProto17","midRuns","Mid","_initProto18","_init_a10","_init_extra_a10","_initProto19","BaseM","SubM","_initProto20","_init_a11","_init_extra_a11","_initProto21","_init_a12","_init_extra_a12","_init_val","_init_extra_val","_init_val2","_init_extra_val2","BaseFO","SubFO","_init_a13","_init_extra_a13","_init_val3","_init_extra_val3","_init_val4","_init_extra_val4","_init_foo","_init_extra_foo","foo","_init_a14","_init_extra_a14","InvalidMemo","run","_initProto22","SuperDuper","dupe","_initProto23","_init_bar","_init_extra_bar","_initProto24","_init_bar2","_init_extra_bar2","Bar","bar","baz","logBar","console","log","Bar2","not"],"sources":["../../src/decorators/memo.test.ts"],"sourcesContent":["import {createEffect, batch, createSignal} from 'solid-js'\nimport {signal} from './signal.js'\nimport {memo} from './memo.js'\nimport {effect} from './effect.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('@memo decorator', () => {\n\t\tit('creates a readonly memo via getter', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo get sum2() {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum2\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum2).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum2).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum2).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Readonly memo cannot be set - should throw in strict mode\n\t\t\texpect(() => {\n\t\t\t\t// @ts-expect-error - intentionally setting readonly property\n\t\t\t\tex.sum2 = 20\n\t\t\t}).toThrow()\n\t\t})\n\n\t\tit('creates a writable memo via getter+setter', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo get sum2() {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t\t@memo set sum2(_val: number) {}\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum2\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum2).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum2).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum2).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Writable memo can be set directly\n\t\t\tex.sum2 = 20\n\t\t\texpect(ex.sum2).toBe(20)\n\t\t\texpect(lastSum).toBe(20)\n\t\t\texpect(count).toBe(3)\n\t\t})\n\n\t\tit('creates a readonly memo via accessor function value', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo accessor sum3 = () => this.a + this.b\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet runs = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum3()\n\t\t\t\truns++\n\t\t\t})\n\n\t\t\texpect(ex.sum3()).toBe(3)\n\t\t\texpect(runs).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum3()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(runs).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum3()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(runs).toBe(2) // count should still be 2, not 3\n\n\t\t\t// @ts-expect-error Readonly memo cannot be set - should throw\n\t\t\texpect(() => ex.sum3(20)).toThrow()\n\t\t})\n\n\t\tit('creates a writable memo via accessor function value', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo accessor sum3 = (_val?: number) => this.a + this.b\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum3()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum3()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum3()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum3()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Writable memo can be set directly\n\t\t\tex.sum3(20)\n\t\t\texpect(ex.sum3()).toBe(20)\n\t\t\texpect(lastSum).toBe(20)\n\t\t\texpect(count).toBe(3)\n\t\t})\n\n\t\tit('creates a readonly memo via method', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo sum4() {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum4()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum4()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum4()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum4()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Readonly memo cannot be set - should throw\n\t\t\texpect(() => {\n\t\t\t\t// @ts-expect-error - intentionally setting readonly memo\n\t\t\t\tex.sum4(20)\n\t\t\t}).toThrow()\n\t\t})\n\n\t\tit('creates a writable memo via method', () => {\n\t\t\tclass Example {\n\t\t\t\t@signal a = 1\n\t\t\t\t@signal b = 2\n\n\t\t\t\t@memo sum4(_val?: number) {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst ex = new Example()\n\t\t\tlet count = 0\n\t\t\tlet lastSum = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastSum = ex.sum4()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(ex.sum4()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tex.a = 5\n\t\t\texpect(ex.sum4()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2)\n\n\t\t\t// This should not trigger the effect since the computed value doesn't change (still 7)\n\t\t\tbatch(() => {\n\t\t\t\tex.a = 3\n\t\t\t\tex.b = 4\n\t\t\t})\n\n\t\t\texpect(ex.sum4()).toBe(7)\n\t\t\texpect(lastSum).toBe(7)\n\t\t\texpect(count).toBe(2) // count should still be 2, not 3\n\n\t\t\t// Writable memo can be set directly\n\t\t\tex.sum4(20)\n\t\t\texpect(ex.sum4()).toBe(20)\n\t\t\texpect(lastSum).toBe(20)\n\t\t\texpect(count).toBe(3)\n\t\t})\n\n\t\tit('memoizes complex computations and only re-runs when dependencies change', () => {\n\t\t\tclass Calculator {\n\t\t\t\tcomputeCount = 0\n\t\t\t\t@signal x = 10\n\t\t\t\t@signal y = 5\n\n\t\t\t\t@memo get result() {\n\t\t\t\t\tthis.computeCount++\n\t\t\t\t\treturn this.x * 2 + this.y\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst calc = new Calculator()\n\n\t\t\texpect(calc.result).toBe(25)\n\t\t\texpect(calc.computeCount).toBe(1)\n\n\t\t\t// Reading again should not re-compute\n\t\t\texpect(calc.result).toBe(25)\n\t\t\texpect(calc.computeCount).toBe(1)\n\n\t\t\t// Changing a dependency should trigger recomputation\n\t\t\tcalc.x = 20\n\t\t\texpect(calc.result).toBe(45)\n\t\t\texpect(calc.computeCount).toBe(2)\n\n\t\t\t// Reading again should not re-compute\n\t\t\texpect(calc.result).toBe(45)\n\t\t\texpect(calc.computeCount).toBe(2)\n\t\t})\n\n\t\tit('works with multiple memo properties', () => {\n\t\t\tclass MultiMemo {\n\t\t\t\t@signal value = 10\n\n\t\t\t\t@memo double() {\n\t\t\t\t\treturn this.value * 2\n\t\t\t\t}\n\t\t\t\t@memo get triple() {\n\t\t\t\t\treturn this.value * 3\n\t\t\t\t}\n\t\t\t\t@memo accessor quadruple = () => this.value * 4\n\t\t\t}\n\n\t\t\tconst mm = new MultiMemo()\n\t\t\tlet doubleCount = 0\n\t\t\tlet tripleCount = 0\n\t\t\tlet quadCount = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tmm.double()\n\t\t\t\tdoubleCount++\n\t\t\t})\n\t\t\tcreateEffect(() => {\n\t\t\t\tmm.triple\n\t\t\t\ttripleCount++\n\t\t\t})\n\t\t\tcreateEffect(() => {\n\t\t\t\tmm.quadruple()\n\t\t\t\tquadCount++\n\t\t\t})\n\n\t\t\texpect(mm.double()).toBe(20)\n\t\t\texpect(mm.triple).toBe(30)\n\t\t\texpect(mm.quadruple()).toBe(40)\n\t\t\texpect(doubleCount).toBe(1)\n\t\t\texpect(tripleCount).toBe(1)\n\t\t\texpect(quadCount).toBe(1)\n\n\t\t\tmm.value = 5\n\t\t\texpect(mm.double()).toBe(10)\n\t\t\texpect(mm.triple).toBe(15)\n\t\t\texpect(mm.quadruple()).toBe(20)\n\t\t\texpect(doubleCount).toBe(2)\n\t\t\texpect(tripleCount).toBe(2)\n\t\t\texpect(quadCount).toBe(2)\n\t\t})\n\n\t\tit('handles memo depending on other memos', () => {\n\t\t\tclass ChainedMemo {\n\t\t\t\t@signal base = 2\n\n\t\t\t\t@memo get squared() {\n\t\t\t\t\treturn this.base * this.base\n\t\t\t\t}\n\n\t\t\t\t@memo get cubed() {\n\t\t\t\t\treturn this.squared * this.base\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst cm = new ChainedMemo()\n\t\t\tlet count = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tcm.cubed\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(cm.squared).toBe(4)\n\t\t\texpect(cm.cubed).toBe(8)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tcm.base = 3\n\t\t\texpect(cm.squared).toBe(9)\n\t\t\texpect(cm.cubed).toBe(27)\n\t\t\texpect(count).toBe(2)\n\t\t})\n\n\t\tit('correctly handles writable getter+setter memo overriding explicit value', () => {\n\t\t\tclass WritableOverride {\n\t\t\t\t@signal a = 5\n\t\t\t\t@signal b = 10\n\n\t\t\t\t@memo get sum() {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t\t@memo set sum(_val: number) {}\n\t\t\t}\n\n\t\t\tconst wo = new WritableOverride()\n\t\t\tlet count = 0\n\t\t\tlet lastValue = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastValue = wo.sum\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(wo.sum).toBe(15)\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(lastValue).toBe(15)\n\n\t\t\t// Override with direct value\n\t\t\two.sum = 100\n\t\t\texpect(wo.sum).toBe(100)\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(lastValue).toBe(100)\n\n\t\t\t// Changing dependencies should still work after override\n\t\t\two.a = 20\n\t\t\t// The memo should now compute based on signals again\n\t\t\texpect(wo.sum).toBe(30) // 20 + 10\n\t\t\texpect(count).toBe(3)\n\t\t\texpect(lastValue).toBe(30)\n\t\t})\n\n\t\tit('correctly handles writable accessor memo overriding explicit value', () => {\n\t\t\tclass WritableOverride {\n\t\t\t\t@signal a = 5\n\t\t\t\t@signal b = 10\n\n\t\t\t\t@memo accessor sum = (_val?: number) => this.a + this.b\n\t\t\t}\n\n\t\t\tconst wo = new WritableOverride()\n\t\t\tlet count = 0\n\t\t\tlet lastValue = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastValue = wo.sum()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(wo.sum()).toBe(15)\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(lastValue).toBe(15)\n\n\t\t\t// Override with direct value\n\t\t\two.sum(100)\n\t\t\texpect(wo.sum()).toBe(100)\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(lastValue).toBe(100)\n\n\t\t\t// Changing dependencies should still work after override\n\t\t\two.a = 20\n\t\t\t// The memo should now compute based on signals again\n\t\t\texpect(wo.sum()).toBe(30) // 20 + 10\n\t\t\texpect(count).toBe(3)\n\t\t\texpect(lastValue).toBe(30)\n\t\t})\n\n\t\tit('correctly handles writable method memo overriding explicit value', () => {\n\t\t\tclass WritableOverride {\n\t\t\t\t@signal a = 5\n\t\t\t\t@signal b = 10\n\n\t\t\t\t@memo sum(_val?: number) {\n\t\t\t\t\treturn this.a + this.b\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst wo = new WritableOverride()\n\t\t\tlet count = 0\n\t\t\tlet lastValue = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tlastValue = wo.sum()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(wo.sum()).toBe(15)\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(lastValue).toBe(15)\n\n\t\t\t// Override with direct value\n\t\t\two.sum(100)\n\t\t\texpect(wo.sum()).toBe(100)\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(lastValue).toBe(100)\n\n\t\t\t// Changing dependencies should still work after override\n\t\t\two.a = 20\n\t\t\t// The memo should now compute based on signals again\n\t\t\texpect(wo.sum()).toBe(30) // 20 + 10\n\t\t\texpect(count).toBe(3)\n\t\t\texpect(lastValue).toBe(30)\n\t\t})\n\n\t\tit('handles memo with no dependencies', () => {\n\t\t\tclass ConstantMemo {\n\t\t\t\t@memo get constant() {\n\t\t\t\t\treturn 42\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst cm = new ConstantMemo()\n\t\t\tlet count = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tcm.constant\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(cm.constant).toBe(42)\n\t\t\texpect(count).toBe(1)\n\n\t\t\t// Reading again should not trigger effect\n\t\t\tconst val = cm.constant\n\t\t\texpect(val).toBe(42)\n\t\t\texpect(count).toBe(1)\n\t\t})\n\n\t\tdescribe('subclass memo overriding/extending', () => {\n\t\t\tit('supports subclass memo extending base memo (getter)', () => {\n\t\t\t\tclass Base {\n\t\t\t\t\t@signal a = 1\n\t\t\t\t\t@memo get baseVal() {\n\t\t\t\t\t\treturn this.a + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@memo override get baseVal() {\n\t\t\t\t\t\treturn super.baseVal + 1 // extend\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst s = new Sub()\n\t\t\t\tlet runs = 0\n\t\t\t\tlet last = 0\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\truns++\n\t\t\t\t\tlast = s.baseVal\n\t\t\t\t})\n\n\t\t\t\texpect(last).toBe(1 + 1 + 1)\n\t\t\t\texpect(runs).toBe(1)\n\n\t\t\t\ts.a = 5\n\t\t\t\texpect(last).toBe(5 + 1 + 1)\n\t\t\t\texpect(runs).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports subclass memo overriding base memo (getter no super)', () => {\n\t\t\t\tclass Base {\n\t\t\t\t\t@signal a = 1\n\t\t\t\t\t@memo get val() {\n\t\t\t\t\t\treturn this.a + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@memo override get val() {\n\t\t\t\t\t\treturn this.a * 2 // override\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst s = new Sub()\n\t\t\t\tlet runs = 0\n\t\t\t\tlet last = 0\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\truns++\n\t\t\t\t\tlast = s.val\n\t\t\t\t})\n\n\t\t\t\texpect(last).toBe(1 * 2)\n\t\t\t\texpect(runs).toBe(1)\n\n\t\t\t\ts.a = 5\n\t\t\t\texpect(last).toBe(5 * 2)\n\t\t\t\texpect(runs).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports getter override with no super', () => {\n\t\t\t\tconst [a, setA] = createSignal(10)\n\t\t\t\tlet baseRuns = 0\n\t\t\t\tlet subRuns = 0\n\n\t\t\t\tclass Base {\n\t\t\t\t\t@memo get val() {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\treturn a() + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@memo override get val() {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\treturn a() + 10\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst o = new Sub()\n\t\t\t\tlet effectRuns = 0\n\t\t\t\tlet effectVal = 0\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\teffectRuns++\n\t\t\t\t\teffectVal = o.val\n\t\t\t\t})\n\n\t\t\t\texpect(effectVal).toBe(10 + 10)\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(effectRuns).toBe(1)\n\n\t\t\t\tsetA(20)\n\t\t\t\texpect(effectVal).toBe(20 + 10)\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(effectRuns).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports multi-level getter extension with super', () => {\n\t\t\t\tconst [a, setA] = createSignal(10)\n\t\t\t\tlet baseRuns = 0\n\t\t\t\tlet midRuns = 0\n\t\t\t\tlet subRuns = 0\n\n\t\t\t\tclass Base {\n\t\t\t\t\t@memo get val() {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\treturn a() + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Mid extends Base {\n\t\t\t\t\t@memo override get val() {\n\t\t\t\t\t\tmidRuns++\n\t\t\t\t\t\treturn super.val + 10\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Mid {\n\t\t\t\t\t@memo override get val() {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\treturn super.val + 100\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst o = new Sub()\n\t\t\t\tlet effectRuns = 0\n\t\t\t\tlet effectVal = 0\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\teffectRuns++\n\t\t\t\t\teffectVal = o.val\n\t\t\t\t})\n\n\t\t\t\texpect(effectVal).toBe(10 + 1 + 10 + 100)\n\t\t\t\texpect(baseRuns).toBe(1)\n\t\t\t\texpect(midRuns).toBe(1)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(effectRuns).toBe(1)\n\n\t\t\t\tsetA(20)\n\t\t\t\texpect(effectVal).toBe(20 + 1 + 10 + 100)\n\t\t\t\texpect(baseRuns).toBe(2)\n\t\t\t\texpect(midRuns).toBe(2)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(effectRuns).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports subclass memo method extension with super', () => {\n\t\t\t\tlet baseRuns = 0\n\n\t\t\t\tclass BaseM {\n\t\t\t\t\t@signal a = 1\n\t\t\t\t\t@memo val() {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\treturn this.a + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet subRuns = 0\n\n\t\t\t\tclass SubM extends BaseM {\n\t\t\t\t\t@memo override val() {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\treturn super.val() + 2\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst s = new SubM()\n\t\t\t\tlet effectRuns = 0\n\t\t\t\tlet last = 0\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\teffectRuns++\n\t\t\t\t\tlast = s.val()\n\t\t\t\t})\n\n\t\t\t\texpect(last).toBe(1 + 1 + 2)\n\t\t\t\texpect(baseRuns).toBe(1)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(effectRuns).toBe(1)\n\n\t\t\t\ts.a = 5\n\t\t\t\texpect(last).toBe(5 + 1 + 2)\n\t\t\t\texpect(baseRuns).toBe(2)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(effectRuns).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports subclass memo method override with no super', () => {\n\t\t\t\tlet baseRuns = 0\n\t\t\t\tlet subRuns = 0\n\n\t\t\t\tclass BaseM {\n\t\t\t\t\t@signal a = 1\n\t\t\t\t\t@memo val() {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\treturn this.a + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass SubM extends BaseM {\n\t\t\t\t\t@memo override val() {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\treturn this.a + 2\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst s = new SubM()\n\t\t\t\tlet effectRuns = 0\n\t\t\t\tlet last = 0\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\teffectRuns++\n\t\t\t\t\tlast = s.val()\n\t\t\t\t})\n\n\t\t\t\texpect(last).toBe(1 + 2)\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(effectRuns).toBe(1)\n\n\t\t\t\ts.a = 5\n\t\t\t\texpect(last).toBe(5 + 2)\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(effectRuns).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports subclass memo auto accessor extension with super', () => {\n\t\t\t\tlet baseRuns = 0\n\t\t\t\tlet subRuns = 0\n\n\t\t\t\tclass BaseFO {\n\t\t\t\t\t@signal a = 1\n\t\t\t\t\t@memo accessor val = () => {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\treturn this.a + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass SubFO extends BaseFO {\n\t\t\t\t\t@memo override accessor val = () => {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\treturn super.val() * 3\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst s = new SubFO()\n\t\t\t\tlet effectRuns = 0\n\t\t\t\tlet last = 0\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\teffectRuns++\n\t\t\t\t\tlast = s.val()\n\t\t\t\t})\n\n\t\t\t\texpect(last).toBe((1 + 1) * 3)\n\t\t\t\texpect(baseRuns).toBe(1)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(effectRuns).toBe(1)\n\n\t\t\t\ts.a = 4\n\t\t\t\texpect(last).toBe((4 + 1) * 3)\n\t\t\t\texpect(baseRuns).toBe(2)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(effectRuns).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports subclass memo auto accessor override with no super', () => {\n\t\t\t\tlet baseRuns = 0\n\t\t\t\tlet subRuns = 0\n\n\t\t\t\tclass BaseFO {\n\t\t\t\t\t@signal a = 1\n\t\t\t\t\t@memo accessor val = () => {\n\t\t\t\t\t\tbaseRuns++\n\t\t\t\t\t\treturn this.a + 1\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass SubFO extends BaseFO {\n\t\t\t\t\t@memo override accessor val = () => {\n\t\t\t\t\t\tsubRuns++\n\t\t\t\t\t\treturn this.a * 3\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst s = new SubFO()\n\t\t\t\tlet effectRuns = 0\n\t\t\t\tlet last = 0\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\teffectRuns++\n\t\t\t\t\tlast = s.val()\n\t\t\t\t})\n\n\t\t\t\texpect(last).toBe(1 * 3)\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(1)\n\t\t\t\texpect(effectRuns).toBe(1)\n\n\t\t\t\ts.a = 4\n\t\t\t\texpect(last).toBe(4 * 3)\n\t\t\t\texpect(baseRuns).toBe(0)\n\t\t\t\texpect(subRuns).toBe(2)\n\t\t\t\texpect(effectRuns).toBe(2)\n\t\t\t})\n\t\t})\n\n\t\tdescribe('invalid usage', () => {\n\t\t\tit('throws on non-function value', () => {\n\t\t\t\tclass Base {\n\t\t\t\t\t// @ts-expect-error non-function value\n\t\t\t\t\t@memo accessor foo = 1\n\t\t\t\t}\n\n\t\t\t\texpect(() => new Base()).toThrow('memo value for \"foo\" is not a function: 1')\n\t\t\t})\n\n\t\t\tit('throws on @memo used on class field', () => {\n\t\t\t\tconst [a] = createSignal(10)\n\n\t\t\t\texpect(() => {\n\t\t\t\t\tclass InvalidMemo {\n\t\t\t\t\t\t// @ts-expect-error @memo not usable on fields\n\t\t\t\t\t\t@memo a = () => a()\n\t\t\t\t\t}\n\t\t\t\t\tnew InvalidMemo()\n\t\t\t\t}).toThrow('@memo is not supported on class fields.')\n\t\t\t})\n\n\t\t\tit('throws on duplicate members', () => {\n\t\t\t\tconst run = () => {\n\t\t\t\t\tclass SuperDuper {\n\t\t\t\t\t\t// @ts-expect-error duplicate member\n\t\t\t\t\t\t@memo get dupe() {\n\t\t\t\t\t\t\treturn 2\n\t\t\t\t\t\t}\n\t\t\t\t\t\t// @ts-expect-error duplicate member\n\t\t\t\t\t\t@memo get dupe() {\n\t\t\t\t\t\t\treturn 3\n\t\t\t\t\t\t}\n\t\t\t\t\t}\n\n\t\t\t\t\tnew SuperDuper()\n\t\t\t\t}\n\n\t\t\t\t// When compiling with Babel, decorators currently throw an error when applied onto multiple members of the same name.\n\t\t\t\texpect(run).toThrow('Decorating two elements with the same name (get dupe) is not supported yet')\n\n\t\t\t\t// When compiling with TypeScript, decorating duplicate members is allowed, and the last one wins.\n\t\t\t\t// expect(run).toThrow(\n\t\t\t\t// \t'@memo decorated member \"dupe\" has already been memoified. This can happen if there are duplicated class members.',\n\t\t\t\t// )\n\n\t\t\t\t// TODO ^ update Babel to latest in @lume/cli, see if decorators on duplicate members work in classy-solid\n\t\t\t})\n\n\t\t\tit('throws due to TDZ when accessing private fields defined after regular fields', () => {\n\t\t\t\tclass Bar {\n\t\t\t\t\t@signal bar = 456\n\n\t\t\t\t\t#baz = 789\n\n\t\t\t\t\t@signal get baz() {\n\t\t\t\t\t\treturn this.#baz\n\t\t\t\t\t}\n\t\t\t\t\t@signal set baz(v) {\n\t\t\t\t\t\tthis.#baz = v\n\t\t\t\t\t}\n\n\t\t\t\t\t// This throws because #baz is used before its initialization\n\t\t\t\t\t// The ordering is:\n\t\t\t\t\t// 1. bar field initialized\n\t\t\t\t\t// 2. bar field runs finalizers because it is last in the ordering of extra initializers (so #baz is not initialized yet)\n\t\t\t\t\t// 3. During the logBar finalizer (executed in the bar extra initializer), the baz getter is accessed, which accesses #baz before it is initialized\n\t\t\t\t\t@effect logBar() {\n\t\t\t\t\t\tconsole.log('this.baz:', this.baz)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\texpect(() => new Bar()).toThrow('Cannot read private member #baz from an object whose class did not declare it')\n\n\t\t\t\t// To work around the problem, place private fields before regular fields:\n\t\t\t\tclass Bar2 {\n\t\t\t\t\t#baz = 789\n\n\t\t\t\t\t@signal bar = 456\n\n\t\t\t\t\t@signal get baz() {\n\t\t\t\t\t\treturn this.#baz\n\t\t\t\t\t}\n\t\t\t\t\t@signal set baz(v) {\n\t\t\t\t\t\tthis.#baz = v\n\t\t\t\t\t}\n\n\t\t\t\t\t@effect logBar() {\n\t\t\t\t\t\tconsole.log('this.baz:', this.baz)\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\texpect(() => new Bar2()).not.toThrow()\n\t\t\t})\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/metadata-shim.d.ts b/dist/decorators/metadata-shim.d.ts new file mode 100644 index 0000000..29ca2cd --- /dev/null +++ b/dist/decorators/metadata-shim.d.ts @@ -0,0 +1,7 @@ +export {}; +declare global { + interface SymbolConstructor { + readonly metadata: unique symbol; + } +} +//# sourceMappingURL=metadata-shim.d.ts.map \ No newline at end of file diff --git a/dist/decorators/metadata-shim.d.ts.map b/dist/decorators/metadata-shim.d.ts.map new file mode 100644 index 0000000..41b8495 --- /dev/null +++ b/dist/decorators/metadata-shim.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"metadata-shim.d.ts","sourceRoot":"","sources":["../../src/decorators/metadata-shim.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAA;AAET,OAAO,CAAC,MAAM,CAAC;IACd,UAAU,iBAAiB;QAC1B,QAAQ,CAAC,QAAQ,EAAE,OAAO,MAAM,CAAA;KAChC;CACD"} \ No newline at end of file diff --git a/dist/decorators/metadata-shim.js b/dist/decorators/metadata-shim.js new file mode 100644 index 0000000..94442b1 --- /dev/null +++ b/dist/decorators/metadata-shim.js @@ -0,0 +1,8 @@ +// Until decorators land natively, we need this shim so that we can use +// decorator metadata. https://github.com/microsoft/TypeScript/issues/53461 + +export {}; // we don't export anything, but this denotes the file as a module to TypeScript + +// @ts-expect-error readonly +Symbol.metadata ??= Symbol.for('Symbol.metadata'); +//# sourceMappingURL=metadata-shim.js.map \ No newline at end of file diff --git a/dist/decorators/metadata-shim.js.map b/dist/decorators/metadata-shim.js.map new file mode 100644 index 0000000..d9d5124 --- /dev/null +++ b/dist/decorators/metadata-shim.js.map @@ -0,0 +1 @@ +{"version":3,"file":"metadata-shim.js","names":["Symbol","metadata","for"],"sources":["../../src/decorators/metadata-shim.ts"],"sourcesContent":["// Until decorators land natively, we need this shim so that we can use\n// decorator metadata. https://github.com/microsoft/TypeScript/issues/53461\n\nexport {} // we don't export anything, but this denotes the file as a module to TypeScript\n\ndeclare global {\n\tinterface SymbolConstructor {\n\t\treadonly metadata: unique symbol\n\t}\n}\n\n// @ts-expect-error readonly\nSymbol.metadata ??= Symbol.for('Symbol.metadata')\n"],"mappings":"AAAA;AACA;;AAEA,UAAS,CAAC;;AAQV;AACAA,MAAM,CAACC,QAAQ,KAAKD,MAAM,CAACE,GAAG,CAAC,iBAAiB,CAAC","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/signal.d.ts b/dist/decorators/signal.d.ts index f3e6b38..e0b651a 100644 --- a/dist/decorators/signal.d.ts +++ b/dist/decorators/signal.d.ts @@ -1,3 +1,4 @@ +import './metadata-shim.js'; /** * @decorator * Decorate properties of a class with `@signal` to back them with Solid diff --git a/dist/decorators/signal.d.ts.map b/dist/decorators/signal.d.ts.map index 3c44ae3..52efb00 100644 --- a/dist/decorators/signal.d.ts.map +++ b/dist/decorators/signal.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/decorators/signal.ts"],"names":[],"mappings":"AAcA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,MAAM,CACrB,KAAK,EAAE,OAAO,EACd,OAAO,EACJ,0BAA0B,GAC1B,2BAA2B,GAC3B,2BAA2B,GAC3B,6BAA6B,GAC9B,GAAG,CAoGL"} \ No newline at end of file +{"version":3,"file":"signal.d.ts","sourceRoot":"","sources":["../../src/decorators/signal.ts"],"names":[],"mappings":"AAKA,OAAO,oBAAoB,CAAA;AAI3B;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,MAAM,CACrB,KAAK,EAAE,OAAO,EACd,OAAO,EACJ,0BAA0B,GAC1B,2BAA2B,GAC3B,2BAA2B,GAC3B,6BAA6B,GAC9B,GAAG,CA2GL"} \ No newline at end of file diff --git a/dist/decorators/signal.js b/dist/decorators/signal.js index 95e60cb..742d8dd 100644 --- a/dist/decorators/signal.js +++ b/dist/decorators/signal.js @@ -1,6 +1,7 @@ -import { $PROXY } from 'solid-js'; -import { getSignal__, trackPropSetAtLeastOnce__, signalify } from '../signals/signalify.js'; -import { sortSignalsMemosInMetadata, isSignalGetter, getMemberStat, finalizeMemos, getSignalsAndMemos } from '../_state.js'; +import { batch } from 'solid-js'; +import { getSignal__, trackPropSetAtLeastOnce__ } from '../signals/signalify.js'; +import { isSignalGetter, getMemberStat, finalizeMembersIfLast, getMembers, signalifyIfNeeded } from '../_state.js'; +import './metadata-shim.js'; const Undefined = Symbol(); /** @@ -38,27 +39,23 @@ export function signal(value, context) { name } = context; const metadata = context.metadata; - const signalsAndMemos = getSignalsAndMemos(metadata); + const signalsAndMemos = getMembers(metadata); if (!(kind === 'field' || kind === 'accessor' || kind === 'getter' || kind === 'setter')) throw new InvalidSignalDecoratorError(); if (kind === 'field') { const stat = getMemberStat(name, 'signal-field', signalsAndMemos); + stat.finalize = function () { + signalifyIfNeeded(this, name, stat); + }; context.addInitializer(function () { - // Special case for Solid proxies: if the object is already a solid proxy, - // all properties are already reactive, no need to signalify. - // @ts-expect-error special indexed access - const proxy = this[$PROXY]; - if (proxy) return; - sortSignalsMemosInMetadata(metadata); - if (stat.applied.get(this)) return; - signalify(this, [name, this[name]]); - stat.applied.set(this, true); - - // If we skipped memoifying prior memo members (accessor and method - // memos) because of prior signal-fields, memo-fields, or - // memo-auto-accessors, finalize those memos now. - finalizeMemos(this, stat, signalsAndMemos); + finalizeMembersIfLast(this, signalsAndMemos); }); - } else if (kind === 'accessor') { + } + + // It's ok that getters/setters/auto-accessors are not finalized the same + // way as with fields above and as with memos/effects, because we do the set + // up during decoration which happens well before any initializers (before + // any memos and effects, so these will be tracked). + else if (kind === 'accessor') { const { get, set @@ -75,11 +72,15 @@ export function signal(value, context) { return get.call(this); }, set: function (newValue) { - set.call(this, newValue); - trackPropSetAtLeastOnce__(this, name); // not needed anymore? test it + // batch, for example in case setter calls super setter, to + // avoid multiple effect runs on a single property set. + batch(() => { + set.call(this, newValue); + trackPropSetAtLeastOnce__(this, name); // not needed anymore? test it - const s = getSignal__(this, signalStorage, initialValue); - s(typeof newValue === 'function' ? () => newValue : newValue); + const s = getSignal__(this, signalStorage, initialValue); + s(typeof newValue === 'function' ? () => newValue : newValue); + }); } }; isSignalGetter.add(newValue.get); @@ -87,16 +88,18 @@ export function signal(value, context) { } else if (kind === 'getter' || kind === 'setter') { const getOrSet = value; const initialValue = Undefined; - if (!Object.hasOwn(metadata, 'getterSetterSignals')) metadata.getterSetterSignals = {}; - const signalsStorages = metadata.getterSetterSignals; + if (!Object.hasOwn(metadata, 'classySolid_getterSetterSignals')) metadata.classySolid_getterSetterSignals = {}; + const signalsStorages = metadata.classySolid_getterSetterSignals; let signalStorage = signalsStorages[name]; if (!signalStorage) signalsStorages[name] = signalStorage = new WeakMap(); - if (!Object.hasOwn(metadata, 'getterSetterPairCounts')) metadata.getterSetterPairCounts = {}; - const pairs = metadata.getterSetterPairCounts; + if (!Object.hasOwn(metadata, 'classySolid_getterSetterPairCounts')) metadata.classySolid_getterSetterPairCounts = {}; + const pairs = metadata.classySolid_getterSetterPairCounts; // Show a helpful error in case someone forgets to decorate both a getter and setter. queueMicrotask(() => { - if (pairs[name] !== 2) throw new MissingSignalDecoratorError(name); + queueMicrotask(() => delete metadata.classySolid_getterSetterPairCounts); + const missing = pairs[name] !== 2; + if (missing) throw new MissingSignalDecoratorError(name); }); if (kind === 'getter') { pairs[name] ??= 0; @@ -110,12 +113,17 @@ export function signal(value, context) { } else { pairs[name] ??= 0; pairs[name]++; - return function (newValue) { - getOrSet.call(this, newValue); - trackPropSetAtLeastOnce__(this, name); - const s = getSignal__(this, signalStorage, initialValue); - s(typeof newValue === 'function' ? () => newValue : newValue); + const newSetter = function (newValue) { + // batch, for example in case setter calls super setter, to + // avoid multiple effect runs on a single property set. + batch(() => { + getOrSet.call(this, newValue); + trackPropSetAtLeastOnce__(this, name); + const s = getSignal__(this, signalStorage, initialValue); + s(typeof newValue === 'function' ? () => newValue : newValue); + }); }; + return newSetter; } } } diff --git a/dist/decorators/signal.js.map b/dist/decorators/signal.js.map index 23235ad..41fa6bd 100644 --- a/dist/decorators/signal.js.map +++ b/dist/decorators/signal.js.map @@ -1 +1 @@ -{"version":3,"file":"signal.js","names":["$PROXY","getSignal__","trackPropSetAtLeastOnce__","signalify","sortSignalsMemosInMetadata","isSignalGetter","getMemberStat","finalizeMemos","getSignalsAndMemos","Undefined","Symbol","signal","value","context","static","Error","kind","name","metadata","signalsAndMemos","InvalidSignalDecoratorError","stat","addInitializer","proxy","applied","get","set","signalStorage","WeakMap","initialValue","undefined","newValue","init","initialVal","call","s","add","getOrSet","Object","hasOwn","getterSetterSignals","signalsStorages","getterSetterPairCounts","pairs","queueMicrotask","MissingSignalDecoratorError","newGetter","constructor","prop","String"],"sources":["../../src/decorators/signal.ts"],"sourcesContent":["import {$PROXY} from 'solid-js'\nimport {getSignal__, trackPropSetAtLeastOnce__, signalify} from '../signals/signalify.js'\nimport type {SignalMetadata} from './types.js'\nimport type {SignalFunction} from '../signals/createSignalFunction.js'\nimport {\n\tsortSignalsMemosInMetadata,\n\tisSignalGetter,\n\tgetMemberStat,\n\tfinalizeMemos,\n\tgetSignalsAndMemos,\n} from '../_state.js'\n\nconst Undefined = Symbol()\n\n/**\n * @decorator\n * Decorate properties of a class with `@signal` to back them with Solid\n * signals, making them reactive.\n *\n * Related: See the Solid.js `createSignal` API for creating standalone signals.\n *\n * Example:\n *\n * ```js\n * import {signal} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * class Counter {\n * ⁣@signal count = 0\n *\n * constructor() {\n * setInterval(() => this.count++, 1000)\n * }\n * }\n *\n * const counter = new Counter()\n *\n * createEffect(() => {\n * console.log('count:', counter.count)\n * })\n * ```\n */\nexport function signal(\n\tvalue: unknown,\n\tcontext:\n\t\t| ClassFieldDecoratorContext\n\t\t| ClassGetterDecoratorContext\n\t\t| ClassSetterDecoratorContext\n\t\t| ClassAccessorDecoratorContext,\n): any {\n\tif (context.static) throw new Error('@signal is not supported on static fields yet.')\n\n\tconst {kind, name} = context\n\tconst metadata = context.metadata as SignalMetadata\n\tconst signalsAndMemos = getSignalsAndMemos(metadata)\n\n\tif (!(kind === 'field' || kind === 'accessor' || kind === 'getter' || kind === 'setter'))\n\t\tthrow new InvalidSignalDecoratorError()\n\n\tif (kind === 'field') {\n\t\tconst stat = getMemberStat(name, 'signal-field', signalsAndMemos)\n\n\t\tcontext.addInitializer(function () {\n\t\t\t// Special case for Solid proxies: if the object is already a solid proxy,\n\t\t\t// all properties are already reactive, no need to signalify.\n\t\t\t// @ts-expect-error special indexed access\n\t\t\tconst proxy = this[$PROXY] as T\n\t\t\tif (proxy) return\n\n\t\t\tsortSignalsMemosInMetadata(metadata)\n\n\t\t\tif (stat.applied.get(this as object)) return\n\t\t\tsignalify(this as object, [name as keyof object, (this as object)[name as keyof object]])\n\t\t\tstat.applied.set(this as object, true)\n\n\t\t\t// If we skipped memoifying prior memo members (accessor and method\n\t\t\t// memos) because of prior signal-fields, memo-fields, or\n\t\t\t// memo-auto-accessors, finalize those memos now.\n\t\t\tfinalizeMemos(this as object, stat, signalsAndMemos)\n\t\t})\n\t} else if (kind === 'accessor') {\n\t\tconst {get, set} = value as {get: () => unknown; set: (v: unknown) => void}\n\t\tconst signalStorage = new WeakMap>()\n\t\tlet initialValue: unknown = undefined\n\n\t\tconst newValue = {\n\t\t\tinit: function (this: object, initialVal: unknown) {\n\t\t\t\tinitialValue = initialVal\n\t\t\t\treturn initialVal\n\t\t\t},\n\t\t\tget: function (this: object): unknown {\n\t\t\t\tgetSignal__(this, signalStorage, initialValue)()\n\t\t\t\treturn get.call(this)\n\t\t\t},\n\t\t\tset: function (this: object, newValue: unknown) {\n\t\t\t\tset.call(this, newValue)\n\t\t\t\ttrackPropSetAtLeastOnce__(this, name) // not needed anymore? test it\n\n\t\t\t\tconst s = getSignal__(this, signalStorage, initialValue)\n\t\t\t\ts(typeof newValue === 'function' ? () => newValue : newValue)\n\t\t\t},\n\t\t}\n\n\t\tisSignalGetter.add(newValue.get)\n\n\t\treturn newValue\n\t} else if (kind === 'getter' || kind === 'setter') {\n\t\tconst getOrSet = value as Function\n\t\tconst initialValue = Undefined\n\n\t\tif (!Object.hasOwn(metadata, 'getterSetterSignals')) metadata.getterSetterSignals = {}\n\t\tconst signalsStorages = metadata.getterSetterSignals!\n\n\t\tlet signalStorage = signalsStorages[name]\n\t\tif (!signalStorage) signalsStorages[name] = signalStorage = new WeakMap>()\n\n\t\tif (!Object.hasOwn(metadata, 'getterSetterPairCounts')) metadata.getterSetterPairCounts = {}\n\t\tconst pairs = metadata.getterSetterPairCounts!\n\n\t\t// Show a helpful error in case someone forgets to decorate both a getter and setter.\n\t\tqueueMicrotask(() => {\n\t\t\tif (pairs[name] !== 2) throw new MissingSignalDecoratorError(name)\n\t\t})\n\n\t\tif (kind === 'getter') {\n\t\t\tpairs[name] ??= 0\n\t\t\tpairs[name]++\n\n\t\t\tconst newGetter = function (this: object): unknown {\n\t\t\t\tgetSignal__(this, signalStorage, initialValue)()\n\t\t\t\treturn getOrSet.call(this)\n\t\t\t}\n\n\t\t\tisSignalGetter.add(newGetter)\n\n\t\t\treturn newGetter\n\t\t} else {\n\t\t\tpairs[name] ??= 0\n\t\t\tpairs[name]++\n\n\t\t\treturn function (this: object, newValue: unknown) {\n\t\t\t\tgetOrSet.call(this, newValue)\n\t\t\t\ttrackPropSetAtLeastOnce__(this, name)\n\n\t\t\t\tconst s = getSignal__(this, signalStorage, initialValue)\n\t\t\t\ts(typeof newValue === 'function' ? () => newValue : newValue)\n\t\t\t}\n\t\t}\n\t}\n}\n\nclass MissingSignalDecoratorError extends Error {\n\tconstructor(prop: PropertyKey) {\n\t\tsuper(\n\t\t\t`Missing @signal decorator on setter or getter for property \"${String(\n\t\t\t\tprop,\n\t\t\t)}\". The @signal decorator will only work on a getter/setter pair with *both* getter and setter decorated with @signal.`,\n\t\t)\n\t}\n}\n\nclass InvalidSignalDecoratorError extends Error {\n\tconstructor() {\n\t\tsuper('The @signal decorator is only for use on fields, getters, setters, and auto accessors.')\n\t}\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"signal.js","names":["batch","getSignal__","trackPropSetAtLeastOnce__","isSignalGetter","getMemberStat","finalizeMembersIfLast","getMembers","signalifyIfNeeded","Undefined","Symbol","signal","value","context","static","Error","kind","name","metadata","signalsAndMemos","InvalidSignalDecoratorError","stat","finalize","addInitializer","get","set","signalStorage","WeakMap","initialValue","undefined","newValue","init","initialVal","call","s","add","getOrSet","Object","hasOwn","classySolid_getterSetterSignals","signalsStorages","classySolid_getterSetterPairCounts","pairs","queueMicrotask","missing","MissingSignalDecoratorError","newGetter","newSetter","constructor","prop","String"],"sources":["../../src/decorators/signal.ts"],"sourcesContent":["import {batch} from 'solid-js'\nimport {getSignal__, trackPropSetAtLeastOnce__} from '../signals/signalify.js'\nimport type {AnyObject, ClassySolidMetadata} from './types.js'\nimport type {SignalFunction} from '../signals/createSignalFunction.js'\nimport {isSignalGetter, getMemberStat, finalizeMembersIfLast, getMembers, signalifyIfNeeded} from '../_state.js'\nimport './metadata-shim.js'\n\nconst Undefined = Symbol()\n\n/**\n * @decorator\n * Decorate properties of a class with `@signal` to back them with Solid\n * signals, making them reactive.\n *\n * Related: See the Solid.js `createSignal` API for creating standalone signals.\n *\n * Example:\n *\n * ```js\n * import {signal} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * class Counter {\n * ⁣@signal count = 0\n *\n * constructor() {\n * setInterval(() => this.count++, 1000)\n * }\n * }\n *\n * const counter = new Counter()\n *\n * createEffect(() => {\n * console.log('count:', counter.count)\n * })\n * ```\n */\nexport function signal(\n\tvalue: unknown,\n\tcontext:\n\t\t| ClassFieldDecoratorContext\n\t\t| ClassGetterDecoratorContext\n\t\t| ClassSetterDecoratorContext\n\t\t| ClassAccessorDecoratorContext,\n): any {\n\tif (context.static) throw new Error('@signal is not supported on static fields yet.')\n\n\tconst {kind, name} = context\n\tconst metadata = context.metadata as ClassySolidMetadata\n\tconst signalsAndMemos = getMembers(metadata)\n\n\tif (!(kind === 'field' || kind === 'accessor' || kind === 'getter' || kind === 'setter'))\n\t\tthrow new InvalidSignalDecoratorError()\n\n\tif (kind === 'field') {\n\t\tconst stat = getMemberStat(name, 'signal-field', signalsAndMemos)\n\n\t\tstat.finalize = function (this: unknown) {\n\t\t\tsignalifyIfNeeded(this as AnyObject, name, stat)\n\t\t}\n\n\t\tcontext.addInitializer(function () {\n\t\t\tfinalizeMembersIfLast(this as AnyObject, signalsAndMemos)\n\t\t})\n\t}\n\n\t// It's ok that getters/setters/auto-accessors are not finalized the same\n\t// way as with fields above and as with memos/effects, because we do the set\n\t// up during decoration which happens well before any initializers (before\n\t// any memos and effects, so these will be tracked).\n\telse if (kind === 'accessor') {\n\t\tconst {get, set} = value as {get: () => unknown; set: (v: unknown) => void}\n\t\tconst signalStorage = new WeakMap>()\n\t\tlet initialValue: unknown = undefined\n\n\t\tconst newValue = {\n\t\t\tinit: function (this: object, initialVal: unknown) {\n\t\t\t\tinitialValue = initialVal\n\t\t\t\treturn initialVal\n\t\t\t},\n\t\t\tget: function (this: object): unknown {\n\t\t\t\tgetSignal__(this, signalStorage, initialValue)()\n\t\t\t\treturn get.call(this)\n\t\t\t},\n\t\t\tset: function (this: object, newValue: unknown) {\n\t\t\t\t// batch, for example in case setter calls super setter, to\n\t\t\t\t// avoid multiple effect runs on a single property set.\n\t\t\t\tbatch(() => {\n\t\t\t\t\tset.call(this, newValue)\n\t\t\t\t\ttrackPropSetAtLeastOnce__(this, name) // not needed anymore? test it\n\n\t\t\t\t\tconst s = getSignal__(this, signalStorage, initialValue)\n\t\t\t\t\ts(typeof newValue === 'function' ? () => newValue : newValue)\n\t\t\t\t})\n\t\t\t},\n\t\t}\n\n\t\tisSignalGetter.add(newValue.get)\n\n\t\treturn newValue\n\t} else if (kind === 'getter' || kind === 'setter') {\n\t\tconst getOrSet = value as Function\n\t\tconst initialValue = Undefined\n\n\t\tif (!Object.hasOwn(metadata, 'classySolid_getterSetterSignals')) metadata.classySolid_getterSetterSignals = {}\n\t\tconst signalsStorages = metadata.classySolid_getterSetterSignals!\n\n\t\tlet signalStorage = signalsStorages[name]\n\t\tif (!signalStorage) signalsStorages[name] = signalStorage = new WeakMap>()\n\n\t\tif (!Object.hasOwn(metadata, 'classySolid_getterSetterPairCounts')) metadata.classySolid_getterSetterPairCounts = {}\n\t\tconst pairs = metadata.classySolid_getterSetterPairCounts!\n\n\t\t// Show a helpful error in case someone forgets to decorate both a getter and setter.\n\t\tqueueMicrotask(() => {\n\t\t\tqueueMicrotask(() => delete metadata.classySolid_getterSetterPairCounts)\n\t\t\tconst missing = pairs[name] !== 2\n\t\t\tif (missing) throw new MissingSignalDecoratorError(name)\n\t\t})\n\n\t\tif (kind === 'getter') {\n\t\t\tpairs[name] ??= 0\n\t\t\tpairs[name]++\n\n\t\t\tconst newGetter = function (this: object): unknown {\n\t\t\t\tgetSignal__(this, signalStorage, initialValue)()\n\t\t\t\treturn getOrSet.call(this)\n\t\t\t}\n\n\t\t\tisSignalGetter.add(newGetter)\n\n\t\t\treturn newGetter\n\t\t} else {\n\t\t\tpairs[name] ??= 0\n\t\t\tpairs[name]++\n\n\t\t\tconst newSetter = function (this: object, newValue: unknown) {\n\t\t\t\t// batch, for example in case setter calls super setter, to\n\t\t\t\t// avoid multiple effect runs on a single property set.\n\t\t\t\tbatch(() => {\n\t\t\t\t\tgetOrSet.call(this, newValue)\n\t\t\t\t\ttrackPropSetAtLeastOnce__(this, name)\n\n\t\t\t\t\tconst s = getSignal__(this, signalStorage, initialValue)\n\t\t\t\t\ts(typeof newValue === 'function' ? () => newValue : newValue)\n\t\t\t\t})\n\t\t\t}\n\n\t\t\treturn newSetter\n\t\t}\n\t}\n}\n\nclass MissingSignalDecoratorError extends Error {\n\tconstructor(prop: PropertyKey) {\n\t\tsuper(\n\t\t\t`Missing @signal decorator on setter or getter for property \"${String(\n\t\t\t\tprop,\n\t\t\t)}\". The @signal decorator will only work on a getter/setter pair with *both* getter and setter decorated with @signal.`,\n\t\t)\n\t}\n}\n\nclass InvalidSignalDecoratorError extends Error {\n\tconstructor() {\n\t\tsuper('The @signal decorator is only for use on fields, getters, setters, and auto accessors.')\n\t}\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/signal.test.js b/dist/decorators/signal.test.js index feb3b25..459ccd7 100644 --- a/dist/decorators/signal.test.js +++ b/dist/decorators/signal.test.js @@ -3,12 +3,15 @@ function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } -import { createEffect } from 'solid-js'; +import { $PROXY, createEffect } from 'solid-js'; +import { createMutable } from 'solid-js/store'; import { testButterflyProps } from '../index.test.js'; import { signal } from './signal.js'; import { signalify } from '../signals/signalify.js'; +import { isSignalGetter } from '../_state.js'; +import { memo } from './memo.js'; describe('classy-solid', () => { - describe('@signal', () => { + describe('@signal decorator', () => { let _initProto, _init_colors, _init_extra_colors, _init_colors2, _init_extra_colors2, _init_wingSize, _init_extra_wingSize, _initProto2, _init_colors3, _init_extra_colors3, _init_colors4, _init_extra_colors4, _init_wingSize2, _init_extra_wingSize2, _init_colors5, _init_extra_colors5, _init_wingSize3, _init_extra_wingSize3, _init_colors6, _get_colors, _set_colors, _init_extra_colors6, _initProto3, _call_colors, _call_colors2, _initProto4, _initProto5; class Butterfly { static { @@ -16,6 +19,11 @@ describe('classy-solid', () => { } colors = (_initProto(this), _init_colors(this, 3)); #wingSize = (_init_extra_colors(this), 2); + + // Stick this here to ensure that nested constructor doesn't + // interfere with decorator behavior mid-way through initialization + // of the wrapper parent class (tested with a subclass) + child = this.constructor !== Butterfly ? new Butterfly() : null; get wingSize() { return this.#wingSize; } @@ -195,14 +203,14 @@ describe('classy-solid', () => { expect(b.getColors()).toBe(5); expect(count).toBe(2); } - const ensure = it; - ensure('overridden fields work as expected', async () => { + it('allows overridden fields to work as expected', async () => { let _init_colors7, _init_extra_colors7; class Mid extends Butterfly { colors = 0; } // ensure subclass did not interfere with functionality of base class + new Butterfly(); // ensure first instantiation doesn't affect later ones const b0 = new Butterfly(); testProp(b0, 'colors', 3, 4, true); expect(Object.getOwnPropertyDescriptor(b0, 'colors')?.get?.call(b0) === 4).toBe(true); // accessor descriptor @@ -338,6 +346,339 @@ describe('classy-solid', () => { expect(count).toBe(2); // it would be 3 if there were an extra signal } }); + it('throws on invalid usage', () => { + expect(() => { + let _init_val, _init_extra_val; + class InvalidStatic { + static { + [_init_val, _init_extra_val] = _applyDecs(this, [], [[signal, 8, "val"]]).e; + } + static val = _init_val(1); + static { + _init_extra_val(); + } + } + new InvalidStatic(); + }).toThrowError('@signal is not supported on static fields yet.'); + expect(() => { + let _initProto8; + class InvalidMethod { + static { + [_initProto8] = _applyDecs(this, [], [[signal, 2, "method"]]).e; + } + constructor() { + _initProto8(this); + } + // @ts-expect-error type error because method is invalid + method() { + return 1; + } + } + new InvalidMethod(); + }).toThrowError('The @signal decorator is only for use on fields, getters, setters, and auto accessors.'); + }); + it('no-ops with Solid proxies to avoid an unnecessary extra signal', () => { + let _initProto9, _init_age, _init_extra_age; + let plain; + let proxy; + class Human { + constructor() { + plain = this; + return proxy = createMutable(this); + } + } + let metadata; + const _signal = (_, context) => { + metadata = context.metadata; + return signal(_, context); + }; + let memoRuns = 0; + class CoolKid extends Human { + static { + [_init_age, _init_extra_age, _initProto9] = _applyDecs(this, [], [[_signal, 0, "age"], [memo, 3, "ageInDogYears"]], 0, void 0, Human).e; + } + constructor(...args) { + super(...args); + _init_extra_age(this); + } + age = (_initProto9(this), _init_age(this, 3)); + get ageInDogYears() { + memoRuns++; + return this.age * 7; + } + } + const kid = new CoolKid(); + + // Verify we got a Solid Proxy. + expect(plain === proxy).toBe(false); + expect(plain[$PROXY] === proxy).toBe(true); + expect(metadata.classySolid_members.find(m => m.name === 'age').applied.get(kid)).toBe(true); + + // Verify it there is not our own signal getter applied (it may be + // the Solid Proxy's, or none, depending on how the Solid Proxy + // implementation goes). + const descriptor = Object.getOwnPropertyDescriptor(kid, 'age'); + const getter = descriptor.get; + expect(isSignalGetter.has(getter)).toBe(false); + let count = 0; + createEffect(() => { + count++; + kid.age; + }); + expect(count).toBe(1); + expect(kid.age).toBe(3); + // check that @memo still works with the Proxy + expect(memoRuns).toBe(1); + expect(kid.ageInDogYears).toBe(21); + kid.age = 4; + expect(count).toBe(2); + expect(kid.age).toBe(4); + // check that @memo still works with the Proxy + expect(memoRuns).toBe(2); + expect(kid.ageInDogYears).toBe(28); + }); + describe('subclass signal overriding/extending', () => { + it('supports subclass signal field extending base signal field', () => { + let _init_val2, _init_extra_val2, _init_val3, _init_extra_val3; + class Base { + static { + [_init_val2, _init_extra_val2] = _applyDecs(this, [], [[signal, 0, "val"]]).e; + } + constructor() { + _init_extra_val2(this); + } + val = _init_val2(this, 1); + } + class Sub extends Base { + static { + [_init_val3, _init_extra_val3] = _applyDecs(this, [], [[signal, 0, "val"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _init_extra_val3(this); + } + // @ts-ignore this is valid in plain JS, TS complains about using field before initialization + val = _init_val3(this, this.val + 1); // override field with initial value from base class + } + const s = new Sub(); + let count = 0; + createEffect(() => { + count++; + s.val; + }); + expect(s.val).toBe(2); + expect(count).toBe(1); + s.val = 5; + expect(s.val).toBe(5); + expect(count).toBe(2); + }); + it('supports subclass signal auto accessor extending base signal auto accessor with super', () => { + let _init_n, _init_extra_n, _init_n2, _init_extra_n2; + class Base { + static { + [_init_n, _init_extra_n] = _applyDecs(this, [], [[signal, 1, "n"]]).e; + } + constructor() { + _init_extra_n(this); + } + #A = _init_n(this, 1); + get n() { + return this.#A; + } + set n(v) { + this.#A = v; + } + } + class Sub extends Base { + static { + [_init_n2, _init_extra_n2] = _applyDecs(this, [], [[signal, 1, "n"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _init_extra_n2(this); + } + #A = _init_n2(this, super.n + 1); // initialize with initial super value + get n() { + return this.#A; + } + set n(v) { + this.#A = v; + } + } + const s = new Sub(); + let count = 0; + createEffect(() => { + count++; + s.n; + }); + expect(s.n).toBe(2); + expect(count).toBe(1); + s.n = 7; + expect(s.n).toBe(7); + expect(count).toBe(2); + }); + it('supports subclass signal getter/setter extending base signal getter/setter with super', () => { + let _initProto0, _initProto1; + class Base { + static { + [_initProto0] = _applyDecs(this, [], [[signal, 3, "n"], [signal, 4, "n"]]).e; + } + #n = (_initProto0(this), 1); + get n() { + return this.#n; + } + set n(v) { + this.#n = v; + } + } + class Sub extends Base { + static { + [_initProto1] = _applyDecs(this, [], [[signal, 3, "n"], [signal, 4, "n"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _initProto1(this); + } + get n() { + return super.n + 1; // extend read + } + set n(v) { + super.n = v + 1; // extend write + } + } + const s = new Sub(); + let count = 0; + let last = 0; + createEffect(() => { + count++; + last = s.n; + }); + expect(last).toBe(1 + 1); + expect(count).toBe(1); + s.n = 10; + expect(last).toBe(10 + 1 + 1); + expect(count).toBe(2); + }); + it('supports multi-level signal getter/setter extension with super', () => { + let _initProto10, _initProto11, _initProto12; + let runs = 0; + class Base { + static { + [_initProto10] = _applyDecs(this, [], [[signal, 3, "val"], [signal, 4, "val"]]).e; + } + _val = (_initProto10(this), 1); + get val() { + return this._val; + } + set val(v) { + this._val = v; + } + } + class Mid extends Base { + static { + [_initProto11] = _applyDecs(this, [], [[signal, 3, "val"], [signal, 4, "val"]], 0, void 0, Base).e; + } + constructor(...args) { + super(...args); + _initProto11(this); + } + get val() { + return super.val + 10; + } + set val(v) { + super.val = v - 10; + } + } + class Sub extends Mid { + static { + [_initProto12] = _applyDecs(this, [], [[signal, 3, "val"], [signal, 4, "val"]], 0, void 0, Mid).e; + } + constructor(...args) { + super(...args); + _initProto12(this); + } + get val() { + return super.val + 100; + } + set val(v) { + super.val = v - 100; + } + } + const o = new Sub(); + createEffect(() => { + runs++; + o.val; + }); + expect(o._val).toBe(1); + expect(o.val).toBe(1 + 10 + 100); + expect(runs).toBe(1); + o.val = 200; + expect(runs).toBe(2); + expect(o._val).toBe(200 - 100 - 10); + expect(o.val).toBe(90 + 10 + 100); + }); + it('supports subclass signal getter/setter overriding base signal getter/setter without super', () => { + let _initProto13, _initProto14; + class Base { + static { + [_initProto13] = _applyDecs(this, [], [[signal, 3, "v"], [signal, 4, "v"]]).e; + } + #v = (_initProto13(this), 1); + get v() { + return this.#v; + } + set v(x) { + this.#v = x; + } + } + class Sub extends Base { + static { + [_initProto14] = _applyDecs(this, [], [[signal, 3, "v"], [signal, 4, "v"]], 0, void 0, Base).e; + } + #y = (_initProto14(this), 100); + get v() { + return this.#y; + } + set v(x) { + this.#y = x; + } + } + const s = new Sub(); + let count = 0; + createEffect(() => { + s.v; + count++; + }); + expect(s.v).toBe(100); + expect(count).toBe(1); + s.v = 50; + expect(s.v).toBe(50); + expect(count).toBe(2); + }); + }); + describe('invalid usage', () => { + it('throws on duplicate members', () => { + const run = () => { + let _init_dupe, _init_extra_dupe, _init_dupe2, _init_extra_dupe2; + class SuperDuper { + static { + [_init_dupe, _init_extra_dupe, _init_dupe2, _init_extra_dupe2] = _applyDecs(this, [], [[signal, 0, "dupe"], [signal, 0, "dupe"]]).e; + } + constructor() { + _init_extra_dupe2(this); + } + dupe = _init_dupe(this, 0); + // @ts-expect-error duplicate member + dupe = (_init_extra_dupe(this), _init_dupe2(this, 0)); + } + new SuperDuper(); + }; + + // This one works the same way whether compiling with Babel or + // TypeScript. See the same tests for @memo and @effect. + expect(run).toThrow('@signal decorated member "dupe" has already been signalified. This can happen if there are duplicated class members.'); + }); + }); }); }); //# sourceMappingURL=signal.test.js.map \ No newline at end of file diff --git a/dist/decorators/signal.test.js.map b/dist/decorators/signal.test.js.map index f10a359..3b66f7c 100644 --- a/dist/decorators/signal.test.js.map +++ b/dist/decorators/signal.test.js.map @@ -1 +1 @@ -{"version":3,"file":"signal.test.js","names":["createEffect","testButterflyProps","signal","signalify","describe","_initProto","_init_colors","_init_extra_colors","_init_colors2","_init_extra_colors2","_init_wingSize","_init_extra_wingSize","_initProto2","_init_colors3","_init_extra_colors3","_init_colors4","_init_extra_colors4","_init_wingSize2","_init_extra_wingSize2","_init_colors5","_init_extra_colors5","_init_wingSize3","_init_extra_wingSize3","_init_colors6","_get_colors","_set_colors","_init_extra_colors6","_initProto3","_call_colors","_call_colors2","_initProto4","_initProto5","Butterfly","_applyDecs","e","colors","wingSize","s","it","b","Butterfly2","constructor","Butterfly3","Butterfly4","A","v","Butterfly5","Butterfly6","o","_","#colors","getColors","setColors","testPrivate","Butterfly7","_colors","Base","Butterfly8","count","expect","toBe","ensure","_init_colors7","_init_extra_colors7","Mid","b0","testProp","Object","getOwnPropertyDescriptor","get","call","SubButterfly","args","m","value","SubSubButterfly","b2","k","startVal","newVal","reactive","_initProto6","_init_colors8","_init_extra_colors8","Insect","double","arg","_init_do","_init_extra_do","Doer","do","doer","newFunc","_initProto7","_init_venomous","_init_extra_venomous","_init_legs","_init_extra_legs","venomous","legs","eyes","n","antennas","i","testNoDuplicateSignal","prop"],"sources":["../../src/decorators/signal.test.ts"],"sourcesContent":["import {createEffect} from 'solid-js'\nimport {testButterflyProps} from '../index.test.js'\nimport {signal} from './signal.js'\nimport {signalify} from '../signals/signalify.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('@signal', () => {\n\t\tclass Butterfly {\n\t\t\t@signal colors = 3\n\n\t\t\t#wingSize = 2\n\n\t\t\t@signal get wingSize() {\n\t\t\t\treturn this.#wingSize\n\t\t\t}\n\t\t\t@signal set wingSize(s: number) {\n\t\t\t\tthis.#wingSize = s\n\t\t\t}\n\t\t}\n\n\t\tit('makes class fields reactive, using class and field/getter/setter decorators', () => {\n\t\t\tconst b = new Butterfly()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly2 {\n\t\t\t@signal colors = 3\n\t\t\t@signal wingSize = 2\n\t\t}\n\n\t\tit('makes class fields reactive, using field decorators without class decorator', () => {\n\t\t\tconst b = new Butterfly2()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly3 {\n\t\t\t@signal colors = 3\n\n\t\t\t#wingSize = 2\n\n\t\t\t@signal get wingSize() {\n\t\t\t\treturn this.#wingSize\n\t\t\t}\n\t\t\t@signal set wingSize(s: number) {\n\t\t\t\tthis.#wingSize = s\n\t\t\t}\n\t\t}\n\n\t\tit('makes class fields reactive, using field/getter/setter decorators without class decorator', () => {\n\t\t\tconst b = new Butterfly3()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly4 {\n\t\t\t@signal colors = 3\n\t\t\t@signal accessor wingSize = 2\n\t\t}\n\n\t\tit('makes class fields reactive, using field/accessor decorators without class decorator', () => {\n\t\t\tconst b = new Butterfly4()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly5 {\n\t\t\t@signal colors = 3\n\t\t\t@signal accessor wingSize = 2\n\t\t}\n\n\t\tit('makes class fields reactive, using field/accessor decorators with class decorator', () => {\n\t\t\tconst b = new Butterfly5()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly6 {\n\t\t\t@signal accessor #colors = 3\n\n\t\t\tgetColors() {\n\t\t\t\treturn this.#colors\n\t\t\t}\n\t\t\tsetColors(v: number) {\n\t\t\t\treturn (this.#colors = v)\n\t\t\t}\n\t\t}\n\n\t\tit('makes private class auto accessor reactive', () => {\n\t\t\tconst b = new Butterfly6()\n\t\t\ttestPrivate(b)\n\t\t})\n\n\t\tclass Butterfly7 {\n\t\t\t#_colors = 3\n\n\t\t\t@signal get #colors() {\n\t\t\t\treturn this.#_colors\n\t\t\t}\n\t\t\t@signal set #colors(v: number) {\n\t\t\t\tthis.#_colors = v\n\t\t\t}\n\n\t\t\tgetColors() {\n\t\t\t\treturn this.#colors\n\t\t\t}\n\t\t\tsetColors(v: number) {\n\t\t\t\treturn (this.#colors = v)\n\t\t\t}\n\t\t}\n\n\t\tit('makes private class getter/setter accessors reactive', () => {\n\t\t\tconst b = new Butterfly7()\n\t\t\ttestPrivate(b)\n\t\t})\n\n\t\tclass Base {\n\t\t\t#colors = 3\n\n\t\t\t@signal get colors() {\n\t\t\t\treturn this.#colors\n\t\t\t}\n\t\t\t@signal set colors(v: number) {\n\t\t\t\tthis.#colors = v\n\t\t\t}\n\t\t}\n\n\t\tclass Butterfly8 extends Base {\n\t\t\t#colors = 3\n\n\t\t\t@signal override get colors() {\n\t\t\t\treturn this.#colors\n\t\t\t}\n\t\t\t@signal override set colors(v: number) {\n\t\t\t\tthis.#colors = v\n\t\t\t}\n\n\t\t\tgetColors() {\n\t\t\t\treturn this.colors\n\t\t\t}\n\t\t\tsetColors(v: number) {\n\t\t\t\treturn (this.colors = v)\n\t\t\t}\n\t\t}\n\n\t\tit('makes overridden class getter/setter accessors reactive', () => {\n\t\t\tconst b = new Butterfly8()\n\t\t\ttestPrivate(b)\n\t\t})\n\n\t\tfunction testPrivate(b: Pick) {\n\t\t\tlet count = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tb.getColors()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(b.getColors()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tb.setColors(5)\n\t\t\texpect(b.getColors()).toBe(5)\n\t\t\texpect(count).toBe(2)\n\t\t}\n\n\t\tconst ensure = it\n\n\t\tensure('overridden fields work as expected', async () => {\n\t\t\tclass Mid extends Butterfly {\n\t\t\t\toverride colors = 0\n\t\t\t}\n\n\t\t\t// ensure subclass did not interfere with functionality of base class\n\t\t\tconst b0 = new Butterfly()\n\t\t\ttestProp(b0, 'colors', 3, 4, true)\n\t\t\texpect(Object.getOwnPropertyDescriptor(b0, 'colors')?.get?.call(b0) === 4).toBe(true) // accessor descriptor\n\n\t\t\tclass SubButterfly extends Mid {\n\t\t\t\t@signal override colors = 123\n\t\t\t}\n\n\t\t\t// ensure subclass did not interfere with functionality of base class\n\t\t\tconst m = new Mid()\n\t\t\ttestProp(m, 'colors', 0, 1, false)\n\t\t\texpect(Object.getOwnPropertyDescriptor(m, 'colors')?.value === 1).toBe(true) // value descriptor\n\n\t\t\tclass SubSubButterfly extends SubButterfly {\n\t\t\t\toverride colors = 456\n\t\t\t}\n\n\t\t\tconst b = new SubButterfly()\n\t\t\ttestButterflyProps(b, 123)\n\n\t\t\tconst b2 = new SubSubButterfly()\n\n\t\t\ttestProp(b2, 'colors', 456, 654, false)\n\t\t})\n\n\t\tfunction testProp(o: T, k: keyof T, startVal: any, newVal: any, reactive = true) {\n\t\t\tlet count = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\to[k]\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(o[k]).toBe(startVal)\n\t\t\texpect(count).toBe(1)\n\n\t\t\to[k] = newVal // should not be a signal, should not trigger\n\n\t\t\texpect(o[k]).toBe(newVal)\n\t\t\texpect(count).toBe(reactive ? 2 : 1)\n\t\t}\n\n\t\tit('does not prevent superclass constructor from receiving subclass constructor args', () => {\n\t\t\tclass Insect {\n\t\t\t\tconstructor(public double: number) {}\n\t\t\t}\n\n\t\t\tclass Butterfly extends Insect {\n\t\t\t\t@signal colors = 3\n\n\t\t\t\t#wingSize = 2\n\n\t\t\t\t@signal get wingSize() {\n\t\t\t\t\treturn this.#wingSize\n\t\t\t\t}\n\t\t\t\t@signal set wingSize(s: number) {\n\t\t\t\t\tthis.#wingSize = s\n\t\t\t\t}\n\n\t\t\t\tconstructor(arg: number) {\n\t\t\t\t\tsuper(arg * 2)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst b = new Butterfly(4)\n\n\t\t\texpect(b.double).toBe(8)\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tit('works with function values', () => {\n\t\t\t// This test ensures that functions are handled propertly, because\n\t\t\t// if passed without being wrapped to a signal setter it will be\n\t\t\t// called immediately with the previous value and be expected to\n\t\t\t// return a new value, instead of being set as the actual new value.\n\n\t\t\tclass Doer {\n\t\t\t\t@signal do: (() => unknown) | null = null\n\t\t\t}\n\n\t\t\tconst doer = new Doer()\n\n\t\t\texpect(doer.do).toBe(null)\n\n\t\t\tconst newFunc = () => 123\n\t\t\tdoer.do = newFunc\n\n\t\t\texpect(doer.do).toBe(newFunc)\n\t\t\texpect(doer.do()).toBe(123)\n\t\t})\n\n\t\tit('prevents duplicate signals for any property', () => {\n\t\t\tclass Insect {\n\t\t\t\t@signal venomous = 0\n\n\t\t\t\t@signal accessor legs = 6\n\n\t\t\t\t#eyes = 10\n\t\t\t\t@signal get eyes() {\n\t\t\t\t\treturn this.#eyes\n\t\t\t\t}\n\t\t\t\t@signal set eyes(n) {\n\t\t\t\t\tthis.#eyes = n\n\t\t\t\t}\n\n\t\t\t\tantennas = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\t// This should not add any extra signals for properties that\n\t\t\t\t\t// are already signalified by the @signal decorator\n\t\t\t\t\tsignalify(this, 'venomous', 'legs', 'eyes', 'antennas')\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst i = new Insect()\n\n\t\t\ttestNoDuplicateSignal(i, 'venomous')\n\t\t\ttestNoDuplicateSignal(i, 'legs')\n\t\t\ttestNoDuplicateSignal(i, 'eyes')\n\t\t\ttestNoDuplicateSignal(i, 'antennas')\n\n\t\t\tfunction testNoDuplicateSignal(o: Insect, prop: keyof Insect) {\n\t\t\t\tlet count = 0\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tcount++\n\t\t\t\t\to[prop]\n\t\t\t\t})\n\t\t\t\texpect(count).toBe(1)\n\t\t\t\to[prop]++\n\t\t\t\texpect(count).toBe(2) // it would be 3 if there were an extra signal\n\t\t\t}\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"signal.test.js","names":["$PROXY","createEffect","createMutable","testButterflyProps","signal","signalify","isSignalGetter","memo","describe","_initProto","_init_colors","_init_extra_colors","_init_colors2","_init_extra_colors2","_init_wingSize","_init_extra_wingSize","_initProto2","_init_colors3","_init_extra_colors3","_init_colors4","_init_extra_colors4","_init_wingSize2","_init_extra_wingSize2","_init_colors5","_init_extra_colors5","_init_wingSize3","_init_extra_wingSize3","_init_colors6","_get_colors","_set_colors","_init_extra_colors6","_initProto3","_call_colors","_call_colors2","_initProto4","_initProto5","Butterfly","_applyDecs","e","colors","wingSize","child","constructor","s","it","b","Butterfly2","Butterfly3","Butterfly4","A","v","Butterfly5","Butterfly6","o","_","#colors","getColors","setColors","testPrivate","Butterfly7","_colors","Base","Butterfly8","count","expect","toBe","_init_colors7","_init_extra_colors7","Mid","b0","testProp","Object","getOwnPropertyDescriptor","get","call","SubButterfly","args","m","value","SubSubButterfly","b2","k","startVal","newVal","reactive","_initProto6","_init_colors8","_init_extra_colors8","Insect","double","arg","_init_do","_init_extra_do","Doer","do","doer","newFunc","_initProto7","_init_venomous","_init_extra_venomous","_init_legs","_init_extra_legs","venomous","legs","eyes","n","antennas","i","testNoDuplicateSignal","prop","_init_val","_init_extra_val","InvalidStatic","val","toThrowError","_initProto8","InvalidMethod","method","_initProto9","_init_age","_init_extra_age","plain","proxy","Human","metadata","_signal","context","memoRuns","CoolKid","age","ageInDogYears","kid","classySolid_members","find","name","applied","descriptor","getter","has","_init_val2","_init_extra_val2","_init_val3","_init_extra_val3","Sub","_init_n","_init_extra_n","_init_n2","_init_extra_n2","_initProto0","_initProto1","last","_initProto10","_initProto11","_initProto12","runs","_val","_initProto13","_initProto14","x","y","run","_init_dupe","_init_extra_dupe","_init_dupe2","_init_extra_dupe2","SuperDuper","dupe","toThrow"],"sources":["../../src/decorators/signal.test.ts"],"sourcesContent":["import {$PROXY, createEffect} from 'solid-js'\nimport {createMutable} from 'solid-js/store'\nimport {testButterflyProps} from '../index.test.js'\nimport {signal} from './signal.js'\nimport {signalify} from '../signals/signalify.js'\nimport type {ClassySolidMetadata} from './types.js'\nimport {isSignalGetter} from '../_state.js'\nimport {memo} from './memo.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('@signal decorator', () => {\n\t\tclass Butterfly {\n\t\t\t@signal colors = 3\n\n\t\t\t#wingSize = 2\n\n\t\t\t// Stick this here to ensure that nested constructor doesn't\n\t\t\t// interfere with decorator behavior mid-way through initialization\n\t\t\t// of the wrapper parent class (tested with a subclass)\n\t\t\tchild: Butterfly | null = this.constructor !== Butterfly ? new Butterfly() : null\n\n\t\t\t@signal get wingSize() {\n\t\t\t\treturn this.#wingSize\n\t\t\t}\n\t\t\t@signal set wingSize(s: number) {\n\t\t\t\tthis.#wingSize = s\n\t\t\t}\n\t\t}\n\n\t\tit('makes class fields reactive, using class and field/getter/setter decorators', () => {\n\t\t\tconst b = new Butterfly()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly2 {\n\t\t\t@signal colors = 3\n\t\t\t@signal wingSize = 2\n\t\t}\n\n\t\tit('makes class fields reactive, using field decorators without class decorator', () => {\n\t\t\tconst b = new Butterfly2()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly3 {\n\t\t\t@signal colors = 3\n\n\t\t\t#wingSize = 2\n\n\t\t\t@signal get wingSize() {\n\t\t\t\treturn this.#wingSize\n\t\t\t}\n\t\t\t@signal set wingSize(s: number) {\n\t\t\t\tthis.#wingSize = s\n\t\t\t}\n\t\t}\n\n\t\tit('makes class fields reactive, using field/getter/setter decorators without class decorator', () => {\n\t\t\tconst b = new Butterfly3()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly4 {\n\t\t\t@signal colors = 3\n\t\t\t@signal accessor wingSize = 2\n\t\t}\n\n\t\tit('makes class fields reactive, using field/accessor decorators without class decorator', () => {\n\t\t\tconst b = new Butterfly4()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly5 {\n\t\t\t@signal colors = 3\n\t\t\t@signal accessor wingSize = 2\n\t\t}\n\n\t\tit('makes class fields reactive, using field/accessor decorators with class decorator', () => {\n\t\t\tconst b = new Butterfly5()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tclass Butterfly6 {\n\t\t\t@signal accessor #colors = 3\n\n\t\t\tgetColors() {\n\t\t\t\treturn this.#colors\n\t\t\t}\n\t\t\tsetColors(v: number) {\n\t\t\t\treturn (this.#colors = v)\n\t\t\t}\n\t\t}\n\n\t\tit('makes private class auto accessor reactive', () => {\n\t\t\tconst b = new Butterfly6()\n\t\t\ttestPrivate(b)\n\t\t})\n\n\t\tclass Butterfly7 {\n\t\t\t#_colors = 3\n\n\t\t\t@signal get #colors() {\n\t\t\t\treturn this.#_colors\n\t\t\t}\n\t\t\t@signal set #colors(v: number) {\n\t\t\t\tthis.#_colors = v\n\t\t\t}\n\n\t\t\tgetColors() {\n\t\t\t\treturn this.#colors\n\t\t\t}\n\t\t\tsetColors(v: number) {\n\t\t\t\treturn (this.#colors = v)\n\t\t\t}\n\t\t}\n\n\t\tit('makes private class getter/setter accessors reactive', () => {\n\t\t\tconst b = new Butterfly7()\n\t\t\ttestPrivate(b)\n\t\t})\n\n\t\tclass Base {\n\t\t\t#colors = 3\n\n\t\t\t@signal get colors() {\n\t\t\t\treturn this.#colors\n\t\t\t}\n\t\t\t@signal set colors(v: number) {\n\t\t\t\tthis.#colors = v\n\t\t\t}\n\t\t}\n\n\t\tclass Butterfly8 extends Base {\n\t\t\t#colors = 3\n\n\t\t\t@signal override get colors() {\n\t\t\t\treturn this.#colors\n\t\t\t}\n\t\t\t@signal override set colors(v: number) {\n\t\t\t\tthis.#colors = v\n\t\t\t}\n\n\t\t\tgetColors() {\n\t\t\t\treturn this.colors\n\t\t\t}\n\t\t\tsetColors(v: number) {\n\t\t\t\treturn (this.colors = v)\n\t\t\t}\n\t\t}\n\n\t\tit('makes overridden class getter/setter accessors reactive', () => {\n\t\t\tconst b = new Butterfly8()\n\t\t\ttestPrivate(b)\n\t\t})\n\n\t\tfunction testPrivate(b: Pick) {\n\t\t\tlet count = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tb.getColors()\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(b.getColors()).toBe(3)\n\t\t\texpect(count).toBe(1)\n\n\t\t\tb.setColors(5)\n\t\t\texpect(b.getColors()).toBe(5)\n\t\t\texpect(count).toBe(2)\n\t\t}\n\n\t\tit('allows overridden fields to work as expected', async () => {\n\t\t\tclass Mid extends Butterfly {\n\t\t\t\toverride colors = 0\n\t\t\t}\n\n\t\t\t// ensure subclass did not interfere with functionality of base class\n\t\t\tnew Butterfly() // ensure first instantiation doesn't affect later ones\n\t\t\tconst b0 = new Butterfly()\n\t\t\ttestProp(b0, 'colors', 3, 4, true)\n\t\t\texpect(Object.getOwnPropertyDescriptor(b0, 'colors')?.get?.call(b0) === 4).toBe(true) // accessor descriptor\n\n\t\t\tclass SubButterfly extends Mid {\n\t\t\t\t@signal override colors = 123\n\t\t\t}\n\n\t\t\t// ensure subclass did not interfere with functionality of base class\n\t\t\tconst m = new Mid()\n\t\t\ttestProp(m, 'colors', 0, 1, false)\n\t\t\texpect(Object.getOwnPropertyDescriptor(m, 'colors')?.value === 1).toBe(true) // value descriptor\n\n\t\t\tclass SubSubButterfly extends SubButterfly {\n\t\t\t\toverride colors = 456\n\t\t\t}\n\n\t\t\tconst b = new SubButterfly()\n\t\t\ttestButterflyProps(b, 123)\n\n\t\t\tconst b2 = new SubSubButterfly()\n\n\t\t\ttestProp(b2, 'colors', 456, 654, false)\n\t\t})\n\n\t\tfunction testProp(o: T, k: keyof T, startVal: any, newVal: any, reactive = true) {\n\t\t\tlet count = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\to[k]\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(o[k]).toBe(startVal)\n\t\t\texpect(count).toBe(1)\n\n\t\t\to[k] = newVal // should not be a signal, should not trigger\n\n\t\t\texpect(o[k]).toBe(newVal)\n\t\t\texpect(count).toBe(reactive ? 2 : 1)\n\t\t}\n\n\t\tit('does not prevent superclass constructor from receiving subclass constructor args', () => {\n\t\t\tclass Insect {\n\t\t\t\tconstructor(public double: number) {}\n\t\t\t}\n\n\t\t\tclass Butterfly extends Insect {\n\t\t\t\t@signal colors = 3\n\n\t\t\t\t#wingSize = 2\n\n\t\t\t\t@signal get wingSize() {\n\t\t\t\t\treturn this.#wingSize\n\t\t\t\t}\n\t\t\t\t@signal set wingSize(s: number) {\n\t\t\t\t\tthis.#wingSize = s\n\t\t\t\t}\n\n\t\t\t\tconstructor(arg: number) {\n\t\t\t\t\tsuper(arg * 2)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst b = new Butterfly(4)\n\n\t\t\texpect(b.double).toBe(8)\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tit('works with function values', () => {\n\t\t\t// This test ensures that functions are handled propertly, because\n\t\t\t// if passed without being wrapped to a signal setter it will be\n\t\t\t// called immediately with the previous value and be expected to\n\t\t\t// return a new value, instead of being set as the actual new value.\n\n\t\t\tclass Doer {\n\t\t\t\t@signal do: (() => unknown) | null = null\n\t\t\t}\n\n\t\t\tconst doer = new Doer()\n\n\t\t\texpect(doer.do).toBe(null)\n\n\t\t\tconst newFunc = () => 123\n\t\t\tdoer.do = newFunc\n\n\t\t\texpect(doer.do).toBe(newFunc)\n\t\t\texpect(doer.do()).toBe(123)\n\t\t})\n\n\t\tit('prevents duplicate signals for any property', () => {\n\t\t\tclass Insect {\n\t\t\t\t@signal venomous = 0\n\n\t\t\t\t@signal accessor legs = 6\n\n\t\t\t\t#eyes = 10\n\t\t\t\t@signal get eyes() {\n\t\t\t\t\treturn this.#eyes\n\t\t\t\t}\n\t\t\t\t@signal set eyes(n) {\n\t\t\t\t\tthis.#eyes = n\n\t\t\t\t}\n\n\t\t\t\tantennas = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\t// This should not add any extra signals for properties that\n\t\t\t\t\t// are already signalified by the @signal decorator\n\t\t\t\t\tsignalify(this, 'venomous', 'legs', 'eyes', 'antennas')\n\t\t\t\t}\n\t\t\t}\n\t\t\tconst i = new Insect()\n\n\t\t\ttestNoDuplicateSignal(i, 'venomous')\n\t\t\ttestNoDuplicateSignal(i, 'legs')\n\t\t\ttestNoDuplicateSignal(i, 'eyes')\n\t\t\ttestNoDuplicateSignal(i, 'antennas')\n\n\t\t\tfunction testNoDuplicateSignal(o: Insect, prop: keyof Insect) {\n\t\t\t\tlet count = 0\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tcount++\n\t\t\t\t\to[prop]\n\t\t\t\t})\n\t\t\t\texpect(count).toBe(1)\n\t\t\t\to[prop]++\n\t\t\t\texpect(count).toBe(2) // it would be 3 if there were an extra signal\n\t\t\t}\n\t\t})\n\n\t\tit('throws on invalid usage', () => {\n\t\t\texpect(() => {\n\t\t\t\tclass InvalidStatic {\n\t\t\t\t\t@signal static val = 1\n\t\t\t\t}\n\t\t\t\tnew InvalidStatic()\n\t\t\t}).toThrowError('@signal is not supported on static fields yet.')\n\n\t\t\texpect(() => {\n\t\t\t\tclass InvalidMethod {\n\t\t\t\t\t// @ts-expect-error type error because method is invalid\n\t\t\t\t\t@signal method() {\n\t\t\t\t\t\treturn 1\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tnew InvalidMethod()\n\t\t\t}).toThrowError('The @signal decorator is only for use on fields, getters, setters, and auto accessors.')\n\t\t})\n\n\t\tit('no-ops with Solid proxies to avoid an unnecessary extra signal', () => {\n\t\t\tlet plain!: Human\n\t\t\tlet proxy!: Human\n\n\t\t\tclass Human {\n\t\t\t\tconstructor() {\n\t\t\t\t\tplain = this\n\t\t\t\t\treturn (proxy = createMutable(this))\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet metadata!: ClassySolidMetadata\n\n\t\t\tconst _signal: typeof signal = (_, context) => {\n\t\t\t\tmetadata = context.metadata as ClassySolidMetadata\n\t\t\t\treturn signal(_, context)\n\t\t\t}\n\n\t\t\tlet memoRuns = 0\n\n\t\t\tclass CoolKid extends Human {\n\t\t\t\t@_signal age = 3\n\n\t\t\t\t@memo get ageInDogYears() {\n\t\t\t\t\tmemoRuns++\n\t\t\t\t\treturn this.age * 7\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst kid = new CoolKid()\n\n\t\t\t// Verify we got a Solid Proxy.\n\t\t\texpect(plain === proxy).toBe(false)\n\t\t\texpect((plain as any)[$PROXY] === proxy).toBe(true)\n\n\t\t\texpect(metadata.classySolid_members!.find(m => m.name === 'age')!.applied.get(kid)).toBe(true)\n\n\t\t\t// Verify it there is not our own signal getter applied (it may be\n\t\t\t// the Solid Proxy's, or none, depending on how the Solid Proxy\n\t\t\t// implementation goes).\n\t\t\tconst descriptor = Object.getOwnPropertyDescriptor(kid, 'age')\n\t\t\tconst getter = descriptor!.get!\n\t\t\texpect(isSignalGetter.has(getter)).toBe(false)\n\n\t\t\tlet count = 0\n\t\t\tcreateEffect(() => {\n\t\t\t\tcount++\n\t\t\t\tkid.age\n\t\t\t})\n\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(kid.age).toBe(3)\n\t\t\t// check that @memo still works with the Proxy\n\t\t\texpect(memoRuns).toBe(1)\n\t\t\texpect(kid.ageInDogYears).toBe(21)\n\n\t\t\tkid.age = 4\n\n\t\t\texpect(count).toBe(2)\n\t\t\texpect(kid.age).toBe(4)\n\t\t\t// check that @memo still works with the Proxy\n\t\t\texpect(memoRuns).toBe(2)\n\t\t\texpect(kid.ageInDogYears).toBe(28)\n\t\t})\n\n\t\tdescribe('subclass signal overriding/extending', () => {\n\t\t\tit('supports subclass signal field extending base signal field', () => {\n\t\t\t\tclass Base {\n\t\t\t\t\t@signal val = 1\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t// @ts-ignore this is valid in plain JS, TS complains about using field before initialization\n\t\t\t\t\t@signal override val = this.val + 1 // override field with initial value from base class\n\t\t\t\t}\n\n\t\t\t\tconst s = new Sub()\n\t\t\t\tlet count = 0\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tcount++\n\t\t\t\t\ts.val\n\t\t\t\t})\n\n\t\t\t\texpect(s.val).toBe(2)\n\t\t\t\texpect(count).toBe(1)\n\n\t\t\t\ts.val = 5\n\t\t\t\texpect(s.val).toBe(5)\n\t\t\t\texpect(count).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports subclass signal auto accessor extending base signal auto accessor with super', () => {\n\t\t\t\tclass Base {\n\t\t\t\t\t@signal accessor n = 1\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@signal override accessor n = super.n + 1 // initialize with initial super value\n\t\t\t\t}\n\n\t\t\t\tconst s = new Sub()\n\t\t\t\tlet count = 0\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tcount++\n\t\t\t\t\ts.n\n\t\t\t\t})\n\n\t\t\t\texpect(s.n).toBe(2)\n\t\t\t\texpect(count).toBe(1)\n\n\t\t\t\ts.n = 7\n\t\t\t\texpect(s.n).toBe(7)\n\t\t\t\texpect(count).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports subclass signal getter/setter extending base signal getter/setter with super', () => {\n\t\t\t\tclass Base {\n\t\t\t\t\t#n = 1\n\t\t\t\t\t@signal get n() {\n\t\t\t\t\t\treturn this.#n\n\t\t\t\t\t}\n\t\t\t\t\t@signal set n(v: number) {\n\t\t\t\t\t\tthis.#n = v\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t@signal override get n() {\n\t\t\t\t\t\treturn super.n + 1 // extend read\n\t\t\t\t\t}\n\t\t\t\t\t@signal override set n(v: number) {\n\t\t\t\t\t\tsuper.n = v + 1 // extend write\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst s = new Sub()\n\t\t\t\tlet count = 0\n\t\t\t\tlet last = 0\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tcount++\n\t\t\t\t\tlast = s.n\n\t\t\t\t})\n\n\t\t\t\texpect(last).toBe(1 + 1)\n\t\t\t\texpect(count).toBe(1)\n\n\t\t\t\ts.n = 10\n\t\t\t\texpect(last).toBe(10 + 1 + 1)\n\t\t\t\texpect(count).toBe(2)\n\t\t\t})\n\n\t\t\tit('supports multi-level signal getter/setter extension with super', () => {\n\t\t\t\tlet runs = 0\n\t\t\t\tclass Base {\n\t\t\t\t\t_val = 1\n\t\t\t\t\t@signal get val() {\n\t\t\t\t\t\treturn this._val\n\t\t\t\t\t}\n\t\t\t\t\t@signal set val(v) {\n\t\t\t\t\t\tthis._val = v\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tclass Mid extends Base {\n\t\t\t\t\t@signal override get val() {\n\t\t\t\t\t\treturn super.val + 10\n\t\t\t\t\t}\n\t\t\t\t\t@signal override set val(v) {\n\t\t\t\t\t\tsuper.val = v - 10\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tclass Sub extends Mid {\n\t\t\t\t\t@signal override get val() {\n\t\t\t\t\t\treturn super.val + 100\n\t\t\t\t\t}\n\t\t\t\t\t@signal override set val(v) {\n\t\t\t\t\t\tsuper.val = v - 100\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tconst o = new Sub()\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\truns++\n\t\t\t\t\to.val\n\t\t\t\t})\n\n\t\t\t\texpect(o._val).toBe(1)\n\t\t\t\texpect(o.val).toBe(1 + 10 + 100)\n\t\t\t\texpect(runs).toBe(1)\n\n\t\t\t\to.val = 200\n\t\t\t\texpect(runs).toBe(2)\n\t\t\t\texpect(o._val).toBe(200 - 100 - 10)\n\t\t\t\texpect(o.val).toBe(90 + 10 + 100)\n\t\t\t})\n\n\t\t\tit('supports subclass signal getter/setter overriding base signal getter/setter without super', () => {\n\t\t\t\tclass Base {\n\t\t\t\t\t#v = 1\n\t\t\t\t\t@signal get v() {\n\t\t\t\t\t\treturn this.#v\n\t\t\t\t\t}\n\t\t\t\t\t@signal set v(x: number) {\n\t\t\t\t\t\tthis.#v = x\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Sub extends Base {\n\t\t\t\t\t#y = 100\n\t\t\t\t\t@signal override get v() {\n\t\t\t\t\t\treturn this.#y\n\t\t\t\t\t}\n\t\t\t\t\t@signal override set v(x: number) {\n\t\t\t\t\t\tthis.#y = x\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst s = new Sub()\n\t\t\t\tlet count = 0\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\ts.v\n\t\t\t\t\tcount++\n\t\t\t\t})\n\n\t\t\t\texpect(s.v).toBe(100)\n\t\t\t\texpect(count).toBe(1)\n\n\t\t\t\ts.v = 50\n\t\t\t\texpect(s.v).toBe(50)\n\t\t\t\texpect(count).toBe(2)\n\t\t\t})\n\t\t})\n\n\t\tdescribe('invalid usage', () => {\n\t\t\tit('throws on duplicate members', () => {\n\t\t\t\tconst run = () => {\n\t\t\t\t\tclass SuperDuper {\n\t\t\t\t\t\t@signal dupe = 0\n\t\t\t\t\t\t// @ts-expect-error duplicate member\n\t\t\t\t\t\t@signal dupe = 0\n\t\t\t\t\t}\n\n\t\t\t\t\tnew SuperDuper()\n\t\t\t\t}\n\n\t\t\t\t// This one works the same way whether compiling with Babel or\n\t\t\t\t// TypeScript. See the same tests for @memo and @effect.\n\t\t\t\texpect(run).toThrow(\n\t\t\t\t\t'@signal decorated member \"dupe\" has already been signalified. This can happen if there are duplicated class members.',\n\t\t\t\t)\n\t\t\t})\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/types.d.ts b/dist/decorators/types.d.ts index abbd426..0526227 100644 --- a/dist/decorators/types.d.ts +++ b/dist/decorators/types.d.ts @@ -1,5 +1,6 @@ import type { Constructor } from 'lowclass/dist/Constructor.js'; import type { SignalFunction } from '../signals/createSignalFunction.js'; +export type AnyObject = Record; export type DecoratedValue = Constructor | Function | ClassAccessorDecoratorTarget | undefined; export type PropKey = string | symbol; export type SupportedKind = 'field' | 'getter' | 'setter'; @@ -7,14 +8,20 @@ export interface PropSpec { initialValue: unknown; kind: SupportedKind; } -export type SignalOrMemoType = 'signal-field' | 'memo-field' | 'memo-accessor' | 'memo-auto-accessor' | 'memo-method'; -export type SignalMetadata = { - signalFieldsAndMemos?: Array<[key: PropKey, stat: { - type: SignalOrMemoType; - applied: WeakMap; - }]>; - getterSetterSignals?: Record> | undefined>; - getterSetterPairCounts: { +export type SignalOrMemoType = 'signal-field' | 'memo-auto-accessor' | 'memo-accessor' | 'memo-method' | 'effect-auto-accessor' | 'effect-method'; +export type MetadataMembers = Array; +export type MemberStat = { + type: SignalOrMemoType; + name: PropKey; + applied: WeakMap; + finalize?(this: AnyObject): void; + value?: unknown; +}; +export type ClassySolidMetadata = { + __proto__: ClassySolidMetadata; + classySolid_members?: MetadataMembers; + classySolid_getterSetterSignals?: Record> | undefined>; + classySolid_getterSetterPairCounts?: { [key: PropKey]: 0 | 1 | 2; }; }; diff --git a/dist/decorators/types.d.ts.map b/dist/decorators/types.d.ts.map index d8a9db7..b11f1b6 100644 --- a/dist/decorators/types.d.ts.map +++ b/dist/decorators/types.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/decorators/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAA;AAC7D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oCAAoC,CAAA;AAEtE,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,4BAA4B,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;AAE/G,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;AAErC,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAIzD,MAAM,WAAW,QAAQ;IACxB,YAAY,EAAE,OAAO,CAAA;IACrB,IAAI,EAAE,aAAa,CAAA;CACnB;AAED,MAAM,MAAM,gBAAgB,GAAG,cAAc,GAAG,YAAY,GAAG,eAAe,GAAG,oBAAoB,GAAG,aAAa,CAAA;AAErH,MAAM,MAAM,cAAc,GAAG;IAC5B,oBAAoB,CAAC,EAAE,KAAK,CAAC,CAAC,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE;QAAC,IAAI,EAAE,gBAAgB,CAAC;QAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;KAAC,CAAC,CAAC,CAAA;IAE/G,mBAAmB,CAAC,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IAE3F,sBAAsB,EAAE;QAAC,CAAC,GAAG,EAAE,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;KAAC,CAAA;CACnD,CAAA"} \ No newline at end of file +{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/decorators/types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,WAAW,EAAC,MAAM,8BAA8B,CAAA;AAC7D,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,oCAAoC,CAAA;AAEtE,MAAM,MAAM,SAAS,GAAG,MAAM,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;AAEpD,MAAM,MAAM,cAAc,GAAG,WAAW,GAAG,QAAQ,GAAG,4BAA4B,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,SAAS,CAAA;AAE/G,MAAM,MAAM,OAAO,GAAG,MAAM,GAAG,MAAM,CAAA;AAErC,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,QAAQ,GAAG,QAAQ,CAAA;AAIzD,MAAM,WAAW,QAAQ;IACxB,YAAY,EAAE,OAAO,CAAA;IACrB,IAAI,EAAE,aAAa,CAAA;CACnB;AAED,MAAM,MAAM,gBAAgB,GACzB,cAAc,GACd,oBAAoB,GACpB,eAAe,GACf,aAAa,GACb,sBAAsB,GACtB,eAAe,CAAA;AAElB,MAAM,MAAM,eAAe,GAAG,KAAK,CAAC,UAAU,CAAC,CAAA;AAE/C,MAAM,MAAM,UAAU,GAAG;IACxB,IAAI,EAAE,gBAAgB,CAAA;IACtB,IAAI,EAAE,OAAO,CAAA;IACb,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACjC,QAAQ,CAAC,CAAC,IAAI,EAAE,SAAS,GAAG,IAAI,CAAA;IAChC,KAAK,CAAC,EAAE,OAAO,CAAA;CACf,CAAA;AAED,MAAM,MAAM,mBAAmB,GAAG;IACjC,SAAS,EAAE,mBAAmB,CAAA;IAC9B,mBAAmB,CAAC,EAAE,eAAe,CAAA;IACrC,+BAA+B,CAAC,EAAE,MAAM,CAAC,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,GAAG,SAAS,CAAC,CAAA;IACvG,kCAAkC,CAAC,EAAE;QAAC,CAAC,GAAG,EAAE,OAAO,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAA;KAAC,CAAA;CAChE,CAAA"} \ No newline at end of file diff --git a/dist/decorators/types.js.map b/dist/decorators/types.js.map index 4c9eda7..572560e 100644 --- a/dist/decorators/types.js.map +++ b/dist/decorators/types.js.map @@ -1 +1 @@ -{"version":3,"file":"types.js","names":[],"sources":["../../src/decorators/types.ts"],"sourcesContent":["import type {Constructor} from 'lowclass/dist/Constructor.js'\nimport type {SignalFunction} from '../signals/createSignalFunction.js'\n\nexport type DecoratedValue = Constructor | Function | ClassAccessorDecoratorTarget | undefined\n\nexport type PropKey = string | symbol\n\nexport type SupportedKind = 'field' | 'getter' | 'setter'\n\n// If we add options for `@signal` later (f.e. `@signal({equals: false})`),\n// those options can go in here too.\nexport interface PropSpec {\n\tinitialValue: unknown\n\tkind: SupportedKind\n}\n\nexport type SignalOrMemoType = 'signal-field' | 'memo-field' | 'memo-accessor' | 'memo-auto-accessor' | 'memo-method'\n\nexport type SignalMetadata = {\n\tsignalFieldsAndMemos?: Array<[key: PropKey, stat: {type: SignalOrMemoType; applied: WeakMap}]>\n\n\tgetterSetterSignals?: Record> | undefined>\n\n\tgetterSetterPairCounts: {[key: PropKey]: 0 | 1 | 2}\n}\n"],"mappings":"","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"types.js","names":[],"sources":["../../src/decorators/types.ts"],"sourcesContent":["import type {Constructor} from 'lowclass/dist/Constructor.js'\nimport type {SignalFunction} from '../signals/createSignalFunction.js'\n\nexport type AnyObject = Record\n\nexport type DecoratedValue = Constructor | Function | ClassAccessorDecoratorTarget | undefined\n\nexport type PropKey = string | symbol\n\nexport type SupportedKind = 'field' | 'getter' | 'setter'\n\n// If we add options for `@signal` later (f.e. `@signal({equals: false})`),\n// those options can go in here too.\nexport interface PropSpec {\n\tinitialValue: unknown\n\tkind: SupportedKind\n}\n\nexport type SignalOrMemoType =\n\t| 'signal-field'\n\t| 'memo-auto-accessor'\n\t| 'memo-accessor'\n\t| 'memo-method'\n\t| 'effect-auto-accessor'\n\t| 'effect-method'\n\nexport type MetadataMembers = Array\n\nexport type MemberStat = {\n\ttype: SignalOrMemoType\n\tname: PropKey\n\tapplied: WeakMap\n\tfinalize?(this: AnyObject): void\n\tvalue?: unknown\n}\n\nexport type ClassySolidMetadata = {\n\t__proto__: ClassySolidMetadata\n\tclassySolid_members?: MetadataMembers\n\tclassySolid_getterSetterSignals?: Record> | undefined>\n\tclassySolid_getterSetterPairCounts?: {[key: PropKey]: 0 | 1 | 2}\n}\n"],"mappings":"","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/untracked.d.ts b/dist/decorators/untracked.d.ts index 5bd63a2..90ba01a 100644 --- a/dist/decorators/untracked.d.ts +++ b/dist/decorators/untracked.d.ts @@ -1,4 +1,5 @@ import type { AnyConstructor } from 'lowclass/dist/Constructor.js'; +import './metadata-shim.js'; /** * A decorator that makes a class's contructor untracked. * diff --git a/dist/decorators/untracked.d.ts.map b/dist/decorators/untracked.d.ts.map index a2bef10..ae377fa 100644 --- a/dist/decorators/untracked.d.ts.map +++ b/dist/decorators/untracked.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"untracked.d.ts","sourceRoot":"","sources":["../../src/decorators/untracked.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAGhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,GAAG,SAAS,GAAG,GAAG,CAwBhG"} \ No newline at end of file +{"version":3,"file":"untracked.d.ts","sourceRoot":"","sources":["../../src/decorators/untracked.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAEhE,OAAO,oBAAoB,CAAA;AAE3B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAyDG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,GAAG,SAAS,GAAG,GAAG,CAwBhG"} \ No newline at end of file diff --git a/dist/decorators/untracked.js b/dist/decorators/untracked.js index efcc361..44245e5 100644 --- a/dist/decorators/untracked.js +++ b/dist/decorators/untracked.js @@ -1,4 +1,5 @@ import { getListener, untrack } from 'solid-js'; +import './metadata-shim.js'; /** * A decorator that makes a class's contructor untracked. diff --git a/dist/decorators/untracked.js.map b/dist/decorators/untracked.js.map index 224235f..e9bb3e5 100644 --- a/dist/decorators/untracked.js.map +++ b/dist/decorators/untracked.js.map @@ -1 +1 @@ -{"version":3,"file":"untracked.js","names":["getListener","untrack","untracked","value","context","kind","TypeError","Class","ReactiveDecorator","constructor","args","instance","Reflect","construct","new","target"],"sources":["../../src/decorators/untracked.ts"],"sourcesContent":["import type {AnyConstructor} from 'lowclass/dist/Constructor.js'\nimport {getListener, untrack} from 'solid-js'\n\n/**\n * A decorator that makes a class's contructor untracked.\n *\n * Sometimes, not typically, you may want to ensure that when a class is\n * instantiated, any signal reads that happen during the constructor do not\n * track those reads.\n *\n * Normally you do not need to read signals during construction, but if you do,\n * you should use `@untracked` to avoid accidentally creating dependencies on\n * those signals for any effects that instantiate the class (therefore avoiding\n * infinite loops).\n *\n * Example:\n *\n * ```ts\n * import {untracked, signal} from \"classy-solid\";\n * import {createEffect} from \"solid-js\";\n *\n * ⁣@untracked\n * class Example {\n * ⁣@signal count = 0;\n *\n * constructor() {\n * this.count = this.count + 1; // does not track .count signal read in any outer effect.\n * }\n * }\n *\n * createEffect(() => {\n * // This does not track .count, so this effect will not re-run when .count changes.\n * // If this did track .count, an infinite loop would happen.\n * const example = new Example();\n *\n * createEffect(() => {\n * // This inner effect tracks .count, so it will re-run (independent of the\n * // outer effect) when .count changes.\n * console.log(example.count);\n * });\n * });\n * ```\n *\n * This can also be called manually without decorators:\n *\n * ```ts\n * import {untracked} from \"classy-solid\";\n *\n * const Example = untracked(\n * class {\n * count = 0;\n *\n * constructor() {\n * this.count = this.count + 1; // does not track .count signal read in any outer effect.\n * }\n * }\n * )\n *\n * // ...same usage as above...\n * ```\n */\nexport function untracked(value: AnyConstructor, context: ClassDecoratorContext | undefined): any {\n\t// context may be undefined when unsing untracked() without decorators\n\tif (typeof value !== 'function' || (context && context.kind !== 'class'))\n\t\tthrow new TypeError('The @untracked decorator is only for use on classes.')\n\n\tconst Class = value\n\n\tclass ReactiveDecorator extends Class {\n\t\tconstructor(...args: any[]) {\n\t\t\tlet instance!: ReactiveDecorator\n\n\t\t\t// Ensure that if we're in an effect that `new`ing a class does not\n\t\t\t// track signal reads, otherwise we'll get into an infinite loop. If\n\t\t\t// someone want to trigger an effect based on properties of the\n\t\t\t// `new`ed instance, they can explicitly read the properties\n\t\t\t// themselves in the effect, making their intent clear.\n\t\t\tif (getListener()) untrack(() => (instance = Reflect.construct(Class, args, new.target))) // super()\n\t\t\telse super(...args), (instance = this)\n\n\t\t\treturn instance\n\t\t}\n\t}\n\n\treturn ReactiveDecorator\n}\n"],"mappings":"AACA,SAAQA,WAAW,EAAEC,OAAO,QAAO,UAAU;;AAE7C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,SAASA,CAACC,KAAqB,EAAEC,OAA0C,EAAO;EACjG;EACA,IAAI,OAAOD,KAAK,KAAK,UAAU,IAAKC,OAAO,IAAIA,OAAO,CAACC,IAAI,KAAK,OAAQ,EACvE,MAAM,IAAIC,SAAS,CAAC,sDAAsD,CAAC;EAE5E,MAAMC,KAAK,GAAGJ,KAAK;EAEnB,MAAMK,iBAAiB,SAASD,KAAK,CAAC;IACrCE,WAAWA,CAAC,GAAGC,IAAW,EAAE;MAC3B,IAAIC,QAA4B;;MAEhC;MACA;MACA;MACA;MACA;MACA,IAAIX,WAAW,CAAC,CAAC,EAAEC,OAAO,CAAC,MAAOU,QAAQ,GAAGC,OAAO,CAACC,SAAS,CAACN,KAAK,EAAEG,IAAI,EAAEI,GAAG,CAACC,MAAM,CAAE,CAAC,EAAC;MAAA,KACrF,KAAK,CAAC,GAAGL,IAAI,CAAC,EAAGC,QAAQ,GAAG,IAAK;MAEtC,OAAOA,QAAQ;IAChB;EACD;EAEA,OAAOH,iBAAiB;AACzB","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"untracked.js","names":["getListener","untrack","untracked","value","context","kind","TypeError","Class","ReactiveDecorator","constructor","args","instance","Reflect","construct","new","target"],"sources":["../../src/decorators/untracked.ts"],"sourcesContent":["import type {AnyConstructor} from 'lowclass/dist/Constructor.js'\nimport {getListener, untrack} from 'solid-js'\nimport './metadata-shim.js'\n\n/**\n * A decorator that makes a class's contructor untracked.\n *\n * Sometimes, not typically, you may want to ensure that when a class is\n * instantiated, any signal reads that happen during the constructor do not\n * track those reads.\n *\n * Normally you do not need to read signals during construction, but if you do,\n * you should use `@untracked` to avoid accidentally creating dependencies on\n * those signals for any effects that instantiate the class (therefore avoiding\n * infinite loops).\n *\n * Example:\n *\n * ```ts\n * import {untracked, signal} from \"classy-solid\";\n * import {createEffect} from \"solid-js\";\n *\n * ⁣@untracked\n * class Example {\n * ⁣@signal count = 0;\n *\n * constructor() {\n * this.count = this.count + 1; // does not track .count signal read in any outer effect.\n * }\n * }\n *\n * createEffect(() => {\n * // This does not track .count, so this effect will not re-run when .count changes.\n * // If this did track .count, an infinite loop would happen.\n * const example = new Example();\n *\n * createEffect(() => {\n * // This inner effect tracks .count, so it will re-run (independent of the\n * // outer effect) when .count changes.\n * console.log(example.count);\n * });\n * });\n * ```\n *\n * This can also be called manually without decorators:\n *\n * ```ts\n * import {untracked} from \"classy-solid\";\n *\n * const Example = untracked(\n * class {\n * count = 0;\n *\n * constructor() {\n * this.count = this.count + 1; // does not track .count signal read in any outer effect.\n * }\n * }\n * )\n *\n * // ...same usage as above...\n * ```\n */\nexport function untracked(value: AnyConstructor, context: ClassDecoratorContext | undefined): any {\n\t// context may be undefined when unsing untracked() without decorators\n\tif (typeof value !== 'function' || (context && context.kind !== 'class'))\n\t\tthrow new TypeError('The @untracked decorator is only for use on classes.')\n\n\tconst Class = value\n\n\tclass ReactiveDecorator extends Class {\n\t\tconstructor(...args: any[]) {\n\t\t\tlet instance!: ReactiveDecorator\n\n\t\t\t// Ensure that if we're in an effect that `new`ing a class does not\n\t\t\t// track signal reads, otherwise we'll get into an infinite loop. If\n\t\t\t// someone want to trigger an effect based on properties of the\n\t\t\t// `new`ed instance, they can explicitly read the properties\n\t\t\t// themselves in the effect, making their intent clear.\n\t\t\tif (getListener()) untrack(() => (instance = Reflect.construct(Class, args, new.target))) // super()\n\t\t\telse super(...args), (instance = this)\n\n\t\t\treturn instance\n\t\t}\n\t}\n\n\treturn ReactiveDecorator\n}\n"],"mappings":"AACA,SAAQA,WAAW,EAAEC,OAAO,QAAO,UAAU;AAC7C,OAAO,oBAAoB;;AAE3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,SAASA,CAACC,KAAqB,EAAEC,OAA0C,EAAO;EACjG;EACA,IAAI,OAAOD,KAAK,KAAK,UAAU,IAAKC,OAAO,IAAIA,OAAO,CAACC,IAAI,KAAK,OAAQ,EACvE,MAAM,IAAIC,SAAS,CAAC,sDAAsD,CAAC;EAE5E,MAAMC,KAAK,GAAGJ,KAAK;EAEnB,MAAMK,iBAAiB,SAASD,KAAK,CAAC;IACrCE,WAAWA,CAAC,GAAGC,IAAW,EAAE;MAC3B,IAAIC,QAA4B;;MAEhC;MACA;MACA;MACA;MACA;MACA,IAAIX,WAAW,CAAC,CAAC,EAAEC,OAAO,CAAC,MAAOU,QAAQ,GAAGC,OAAO,CAACC,SAAS,CAACN,KAAK,EAAEG,IAAI,EAAEI,GAAG,CAACC,MAAM,CAAE,CAAC,EAAC;MAAA,KACrF,KAAK,CAAC,GAAGL,IAAI,CAAC,EAAGC,QAAQ,GAAG,IAAK;MAEtC,OAAOA,QAAQ;IAChB;EACD;EAEA,OAAOH,iBAAiB;AACzB","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/untracked.test.js b/dist/decorators/untracked.test.js index 774e377..4bb1162 100644 --- a/dist/decorators/untracked.test.js +++ b/dist/decorators/untracked.test.js @@ -128,7 +128,19 @@ describe('Reactivity Tracking in Constructors', () => { constructor() { _init_extra_amount3(this); } - amount = _init_amount3(this, 3); + amount = _init_amount3(this, (() => { + debugger; + return 3; + })()); + + // @signal accessor yo = 123 + + // @signal get bar() { + // return this + // } + // @signal set bar(v) { + // // do nothing + // } } class Bar extends Foo { static { diff --git a/dist/decorators/untracked.test.js.map b/dist/decorators/untracked.test.js.map index 895e0da..b6950a0 100644 --- a/dist/decorators/untracked.test.js.map +++ b/dist/decorators/untracked.test.js.map @@ -1 +1 @@ -{"version":3,"file":"untracked.test.js","names":["createEffect","signal","untracked","memo","reactive","describe","it","_Foo2","_initClass","_init_amount","_init_extra_amount","_initClass2","_init_double","_init_extra_double","_Foo","Foo","e","c","_applyDecs","constructor","amount","_Bar","Bar","double","b","count","noLoop","expect","not","toThrow","toBe","b2","_Foo4","_initClass3","_init_amount2","_init_extra_amount2","_initClass4","_init_double2","_init_extra_double2","_Foo3","_Bar2","_init_amount3","_init_extra_amount3","_initProto","args"],"sources":["../../src/decorators/untracked.test.ts"],"sourcesContent":["import {createEffect} from 'solid-js'\nimport {signal} from './signal.js'\nimport {untracked} from './untracked.js'\nimport {memo} from './memo.js'\nimport {reactive} from './reactive.js'\n\ndescribe('Reactivity Tracking in Constructors', () => {\n\tit('automatically does not track reactivity in constructors when using @untracked', () => {\n\t\t@untracked\n\t\tclass Foo {\n\t\t\t@signal amount = 3\n\t\t}\n\n\t\t@untracked\n\t\tclass Bar extends Foo {\n\t\t\t@signal double = 0\n\n\t\t\tconstructor() {\n\t\t\t\tsuper()\n\t\t\t\tthis.double = this.amount * 2 // this read of .amount should not be tracked\n\t\t\t}\n\t\t}\n\n\t\tlet b: Bar\n\t\tlet count = 0\n\n\t\tfunction noLoop() {\n\t\t\tcreateEffect(() => {\n\t\t\t\tb = new Bar() // this should not track\n\t\t\t\tcount++\n\t\t\t})\n\t\t}\n\n\t\texpect(noLoop).not.toThrow()\n\t\texpect(count).toBe(1)\n\n\t\tconst b2 = b!\n\n\t\tb!.amount = 4 // hence this should not trigger\n\n\t\t// If the effect ran only once initially, not when setting b.colors,\n\t\t// then both variables should reference the same instance\n\t\texpect(count).toBe(1)\n\t\texpect(b!).toBe(b2)\n\t})\n\n\t// deprecated\n\tit('automatically does not track reactivity in constructors when using @reactive', () => {\n\t\t@reactive\n\t\tclass Foo {\n\t\t\t@signal amount = 3\n\t\t}\n\n\t\t@reactive\n\t\tclass Bar extends Foo {\n\t\t\t@signal double = 0\n\n\t\t\tconstructor() {\n\t\t\t\tsuper()\n\t\t\t\tthis.double = this.amount * 2 // this read of .amount should not be tracked\n\t\t\t}\n\t\t}\n\n\t\tlet b: Bar\n\t\tlet count = 0\n\n\t\tfunction noLoop() {\n\t\t\tcreateEffect(() => {\n\t\t\t\tb = new Bar() // this should not track\n\t\t\t\tcount++\n\t\t\t})\n\t\t}\n\n\t\texpect(noLoop).not.toThrow()\n\t\texpect(count).toBe(1)\n\n\t\tconst b2 = b!\n\n\t\tb!.amount = 4 // hence this should not trigger\n\n\t\t// If the effect ran only once initially, not when setting b.colors,\n\t\t// then both variables should reference the same instance\n\t\texpect(count).toBe(1)\n\t\texpect(b!).toBe(b2)\n\t})\n\n\tit('automatically does not track reactivity in constructors when using @memo', () => {\n\t\tclass Foo {\n\t\t\t@signal amount = 3\n\t\t}\n\n\t\tclass Bar extends Foo {\n\t\t\t@memo get double() {\n\t\t\t\treturn this.amount * 2\n\t\t\t}\n\t\t}\n\n\t\tlet b: Bar\n\t\tlet count = 0\n\n\t\tfunction noLoop() {\n\t\t\tcreateEffect(() => {\n\t\t\t\tb = new Bar() // this should not track\n\t\t\t\tcount++\n\t\t\t})\n\t\t}\n\n\t\texpect(noLoop).not.toThrow()\n\t\texpect(count).toBe(1)\n\n\t\tconst b2 = b!\n\n\t\tb!.amount = 4 // hence this should not trigger\n\n\t\t// If the effect ran only once initially, not when setting b.colors,\n\t\t// then both variables should reference the same instance\n\t\texpect(count).toBe(1)\n\t\texpect(b!).toBe(b2)\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"untracked.test.js","names":["createEffect","signal","untracked","memo","reactive","describe","it","_Foo2","_initClass","_init_amount","_init_extra_amount","_initClass2","_init_double","_init_extra_double","_Foo","Foo","e","c","_applyDecs","constructor","amount","_Bar","Bar","double","b","count","noLoop","expect","not","toThrow","toBe","b2","_Foo4","_initClass3","_init_amount2","_init_extra_amount2","_initClass4","_init_double2","_init_extra_double2","_Foo3","_Bar2","_init_amount3","_init_extra_amount3","_initProto","args"],"sources":["../../src/decorators/untracked.test.ts"],"sourcesContent":["import {createEffect} from 'solid-js'\nimport {signal} from './signal.js'\nimport {untracked} from './untracked.js'\nimport {memo} from './memo.js'\nimport {reactive} from './reactive.js'\n\ndescribe('Reactivity Tracking in Constructors', () => {\n\tit('automatically does not track reactivity in constructors when using @untracked', () => {\n\t\t@untracked\n\t\tclass Foo {\n\t\t\t@signal amount = 3\n\t\t}\n\n\t\t@untracked\n\t\tclass Bar extends Foo {\n\t\t\t@signal double = 0\n\n\t\t\tconstructor() {\n\t\t\t\tsuper()\n\t\t\t\tthis.double = this.amount * 2 // this read of .amount should not be tracked\n\t\t\t}\n\t\t}\n\n\t\tlet b: Bar\n\t\tlet count = 0\n\n\t\tfunction noLoop() {\n\t\t\tcreateEffect(() => {\n\t\t\t\tb = new Bar() // this should not track\n\t\t\t\tcount++\n\t\t\t})\n\t\t}\n\n\t\texpect(noLoop).not.toThrow()\n\t\texpect(count).toBe(1)\n\n\t\tconst b2 = b!\n\n\t\tb!.amount = 4 // hence this should not trigger\n\n\t\t// If the effect ran only once initially, not when setting b.colors,\n\t\t// then both variables should reference the same instance\n\t\texpect(count).toBe(1)\n\t\texpect(b!).toBe(b2)\n\t})\n\n\t// deprecated\n\tit('automatically does not track reactivity in constructors when using @reactive', () => {\n\t\t@reactive\n\t\tclass Foo {\n\t\t\t@signal amount = 3\n\t\t}\n\n\t\t@reactive\n\t\tclass Bar extends Foo {\n\t\t\t@signal double = 0\n\n\t\t\tconstructor() {\n\t\t\t\tsuper()\n\t\t\t\tthis.double = this.amount * 2 // this read of .amount should not be tracked\n\t\t\t}\n\t\t}\n\n\t\tlet b: Bar\n\t\tlet count = 0\n\n\t\tfunction noLoop() {\n\t\t\tcreateEffect(() => {\n\t\t\t\tb = new Bar() // this should not track\n\t\t\t\tcount++\n\t\t\t})\n\t\t}\n\n\t\texpect(noLoop).not.toThrow()\n\t\texpect(count).toBe(1)\n\n\t\tconst b2 = b!\n\n\t\tb!.amount = 4 // hence this should not trigger\n\n\t\t// If the effect ran only once initially, not when setting b.colors,\n\t\t// then both variables should reference the same instance\n\t\texpect(count).toBe(1)\n\t\texpect(b!).toBe(b2)\n\t})\n\n\tit('automatically does not track reactivity in constructors when using @memo', () => {\n\t\tclass Foo {\n\t\t\t@signal amount = (() => {\n\t\t\t\tdebugger\n\t\t\t\treturn 3\n\t\t\t})()\n\n\t\t\t// @signal accessor yo = 123\n\n\t\t\t// @signal get bar() {\n\t\t\t// \treturn this\n\t\t\t// }\n\t\t\t// @signal set bar(v) {\n\t\t\t// \t// do nothing\n\t\t\t// }\n\t\t}\n\n\t\tclass Bar extends Foo {\n\t\t\t@memo get double() {\n\t\t\t\treturn this.amount * 2\n\t\t\t}\n\t\t}\n\n\t\tlet b: Bar\n\t\tlet count = 0\n\n\t\tfunction noLoop() {\n\t\t\tcreateEffect(() => {\n\t\t\t\tb = new Bar() // this should not track\n\t\t\t\tcount++\n\t\t\t})\n\t\t}\n\n\t\texpect(noLoop).not.toThrow()\n\t\texpect(count).toBe(1)\n\n\t\tconst b2 = b!\n\n\t\tb!.amount = 4 // hence this should not trigger\n\n\t\t// If the effect ran only once initially, not when setting b.colors,\n\t\t// then both variables should reference the same instance\n\t\texpect(count).toBe(1)\n\t\texpect(b!).toBe(b2)\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/example.d.ts.map b/dist/example.d.ts.map index be9d6c1..d02012b 100644 --- a/dist/example.d.ts.map +++ b/dist/example.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":"AAUA,cACM,GAAG;IACA,GAAG,SAAM;IAEjB,IAAY,KAAK,WAEhB;IACD,IAAI,KAAK,CAAC,CAAC,QAAA,EAEV;CACD;AAuBD,OAAO,EAAC,GAAG,EAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"example.d.ts","sourceRoot":"","sources":["../src/example.ts"],"names":[],"mappings":"AAGA,cAAM,GAAG;IACA,GAAG,SAAM;IAEjB,IAAY,KAAK,WAEhB;IACD,IAAY,KAAK,CAAC,CAAC,QAAA,EAElB;CACD;AAoCD,OAAO,EAAC,GAAG,EAAC,CAAA"} \ No newline at end of file diff --git a/dist/example.js b/dist/example.js index 1ef14d7..9f19fef 100644 --- a/dist/example.js +++ b/dist/example.js @@ -1,36 +1,14 @@ -var _Foo2; -let _initClass, _init_yoo, _init_extra_yoo, _initProto, _initClass2, _init_foo, _init_extra_foo, _initProto2, _initClass3, _init_bar, _init_extra_bar; +let _initProto, _init_foo, _init_extra_foo, _initProto2, _init_bar, _init_extra_bar; function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; } function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } -import { signal, reactive } from './index.js'; +import { signal, effect } from './index.js'; import { createEffect } from 'solid-js'; -let _Other; -class Other { - static { - ({ - e: [_init_yoo, _init_extra_yoo], - c: [_Other, _initClass] - } = _applyDecs(this, [reactive], [[signal, 0, "yoo"]])); - } - constructor() { - _init_extra_yoo(this); - } - yoo = _init_yoo(this, 'hoo'); - static { - _initClass(); - } -} -new _Other(); -let _Foo; class Foo { static { - ({ - e: [_init_foo, _init_extra_foo, _initProto], - c: [_Foo, _initClass2] - } = _applyDecs(this, [reactive], [[signal, 0, "foo"], [signal, 3, "lorem"]])); + [_init_foo, _init_extra_foo, _initProto] = _applyDecs(this, [], [[signal, 0, "foo"], [signal, 3, "lorem"], [signal, 4, "lorem"]]).e; } constructor() { _init_extra_foo(this); @@ -42,36 +20,45 @@ class Foo { set lorem(v) { v; } - static { - _initClass2(); - } } -let _Bar; -class Bar extends (_Foo2 = _Foo) { +class Bar extends Foo { static { - ({ - e: [_init_bar, _init_extra_bar, _initProto2], - c: [_Bar, _initClass3] - } = _applyDecs(this, [reactive], [[signal, 0, "bar"], [signal, 3, "baz"]], 0, void 0, _Foo2)); + [_init_bar, _init_extra_bar, _initProto2] = _applyDecs(this, [], [[signal, 0, "bar"], [signal, 3, "baz"], [signal, 4, "baz"], [effect, 2, "logFoo"], [effect, 2, "logLorem"], [effect, 2, "logBar"], [effect, 2, "logBaz"]], 0, void 0, Foo).e; } - bar = (_initProto2(this), _init_bar(this, 456)); - #baz = (_init_extra_bar(this), 789); + constructor(...args) { + super(...args); + _init_extra_bar(this); + } + // This causes a TDZ error if it comes after bar. See "TDZ" example in signal.test.ts. + // Spec ordering issue: https://github.com/tc39/proposal-decorators/issues/571 + #baz = (_initProto2(this), 789); + bar = _init_bar(this, 456); + + // This would cause a TDZ error. + // #baz = 789 + get baz() { return this.#baz; } set baz(v) { this.#baz = v; } - constructor() { - super(); - console.log('Bar'); + logFoo() { + console.log('this.foo:', this.foo); } - static { - _initClass3(); + logLorem() { + console.log('this.lorem:', this.lorem); + } + logBar() { + console.log('this.bar:', this.bar); + } + logBaz() { + console.log('this.baz:', this.baz); } } -export { _Foo as Foo }; -const b = new _Bar(); +export { Foo }; +console.log('---------'); +const b = new Bar(); createEffect(() => { console.log('b.foo:', b.foo); }); diff --git a/dist/example.js.map b/dist/example.js.map index 5abda98..a555ffd 100644 --- a/dist/example.js.map +++ b/dist/example.js.map @@ -1 +1 @@ -{"version":3,"file":"example.js","names":["signal","reactive","createEffect","_Other","Other","e","_init_yoo","_init_extra_yoo","c","_initClass","_applyDecs","constructor","yoo","_Foo","Foo","_init_foo","_init_extra_foo","_initProto","_initClass2","foo","lorem","v","_Bar","Bar","_Foo2","_init_bar","_init_extra_bar","_initProto2","_initClass3","bar","baz","console","log","b","setInterval"],"sources":["../src/example.ts"],"sourcesContent":["import {signal, reactive} from './index.js'\nimport {createEffect} from 'solid-js'\n\n@reactive\nclass Other {\n\t@signal yoo = 'hoo'\n}\n\nnew Other()\n\n@reactive\nclass Foo {\n\t@signal foo = 123\n\n\t@signal get lorem() {\n\t\treturn 123\n\t}\n\tset lorem(v) {\n\t\tv\n\t}\n}\n\n@reactive\nclass Bar extends Foo {\n\t@signal bar = 456\n\n\t#baz = 789\n\n\t@signal\n\tget baz() {\n\t\treturn this.#baz\n\t}\n\tset baz(v) {\n\t\tthis.#baz = v\n\t}\n\n\tconstructor() {\n\t\tsuper()\n\n\t\tconsole.log('Bar')\n\t}\n}\n\nexport {Foo}\n\nconst b = new Bar()\n\ncreateEffect(() => {\n\tconsole.log('b.foo:', b.foo)\n})\n\ncreateEffect(() => {\n\tconsole.log('b.lorem:', b.lorem)\n})\n\ncreateEffect(() => {\n\tconsole.log('b.bar:', b.bar)\n})\n\ncreateEffect(() => {\n\tconsole.log('b.baz:', b.baz)\n})\n\nsetInterval(() => {\n\tconsole.log('---')\n\tb.foo++\n\tb.bar++\n\tb.baz++\n\tb.lorem++\n}, 1000)\n"],"mappings":";;;;;;;AAAA,SAAQA,MAAM,EAAEC,QAAQ,QAAO,YAAY;AAC3C,SAAQC,YAAY,QAAO,UAAU;AAAA,IAAAC,MAAA;AAErC,MAAAC,KAAA,CACY;EAAA;IAAA;MAAAC,CAAA,GAAAC,SAAA,EAAAC,eAAA;MAAAC,CAAA,GAAAL,MAAA,EAAAM,UAAA;IAAA,IAAAC,UAAA,QADXT,QAAQ,KAEPD,MAAM;EAAA;EAAAW,YAAA;IAAAJ,eAAA;EAAA;EAACK,GAAG,GAAAN,SAAA,OAAG,KAAK;EAAA;IAAAG,UAAA;EAAA;AACpB;AAEA,IAAIL,MAAK,CAAC,CAAC;AAAA,IAAAS,IAAA;AAEX,MAAAC,GAAA,CACU;EAAA;IAAA;MAAAT,CAAA,GAAAU,SAAA,EAAAC,eAAA,EAAAC,UAAA;MAAAT,CAAA,GAAAK,IAAA,EAAAK,WAAA;IAAA,IAAAR,UAAA,QADTT,QAAQ,KAEPD,MAAM,cAENA,MAAM;EAAA;EAAAW,YAAA;IAAAK,eAAA;EAAA;EAFCG,GAAG,IAAAF,UAAA,QAAAF,SAAA,OAAG,GAAG;EAEjB,IAAYK,KAAKA,CAAA,EAAG;IACnB,OAAO,GAAG;EACX;EACA,IAAIA,KAAKA,CAACC,CAAC,EAAE;IACZA,CAAC;EACF;EAAC;IAAAH,WAAA;EAAA;AACF;AAAC,IAAAI,IAAA;AAED,MAAAC,GAAA,UAAAC,KAAA,GACkBV,IAAG,EAAC;EAAA;IAAA;MAAAT,CAAA,GAAAoB,SAAA,EAAAC,eAAA,EAAAC,WAAA;MAAAnB,CAAA,GAAAc,IAAA,EAAAM,WAAA;IAAA,IAAAlB,UAAA,QADrBT,QAAQ,KAEPD,MAAM,cAINA,MAAM,yBAAAwB,KAAA;EAAA;EAJCK,GAAG,IAAAF,WAAA,QAAAF,SAAA,OAAG,GAAG;EAEjB,CAACK,GAAG,IAAAJ,eAAA,QAAG,GAAG;EAEV,IACII,GAAGA,CAAA,EAAG;IACT,OAAO,IAAI,CAAC,CAACA,GAAG;EACjB;EACA,IAAIA,GAAGA,CAACT,CAAC,EAAE;IACV,IAAI,CAAC,CAACS,GAAG,GAAGT,CAAC;EACd;EAEAV,WAAWA,CAAA,EAAG;IACb,KAAK,CAAC,CAAC;IAEPoB,OAAO,CAACC,GAAG,CAAC,KAAK,CAAC;EACnB;EAAC;IAAAJ,WAAA;EAAA;AACF;AAEA,SAAQd,IAAG,IAAHA,GAAG;AAEX,MAAMmB,CAAC,GAAG,IAAIV,IAAG,CAAC,CAAC;AAEnBrB,YAAY,CAAC,MAAM;EAClB6B,OAAO,CAACC,GAAG,CAAC,QAAQ,EAAEC,CAAC,CAACd,GAAG,CAAC;AAC7B,CAAC,CAAC;AAEFjB,YAAY,CAAC,MAAM;EAClB6B,OAAO,CAACC,GAAG,CAAC,UAAU,EAAEC,CAAC,CAACb,KAAK,CAAC;AACjC,CAAC,CAAC;AAEFlB,YAAY,CAAC,MAAM;EAClB6B,OAAO,CAACC,GAAG,CAAC,QAAQ,EAAEC,CAAC,CAACJ,GAAG,CAAC;AAC7B,CAAC,CAAC;AAEF3B,YAAY,CAAC,MAAM;EAClB6B,OAAO,CAACC,GAAG,CAAC,QAAQ,EAAEC,CAAC,CAACH,GAAG,CAAC;AAC7B,CAAC,CAAC;AAEFI,WAAW,CAAC,MAAM;EACjBH,OAAO,CAACC,GAAG,CAAC,KAAK,CAAC;EAClBC,CAAC,CAACd,GAAG,EAAE;EACPc,CAAC,CAACJ,GAAG,EAAE;EACPI,CAAC,CAACH,GAAG,EAAE;EACPG,CAAC,CAACb,KAAK,EAAE;AACV,CAAC,EAAE,IAAI,CAAC","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"example.js","names":["signal","effect","createEffect","Foo","_init_foo","_init_extra_foo","_initProto","_applyDecs","e","constructor","foo","lorem","v","Bar","_init_bar","_init_extra_bar","_initProto2","args","baz","bar","logFoo","console","log","logLorem","logBar","logBaz","b","setInterval"],"sources":["../src/example.ts"],"sourcesContent":["import {signal, effect} from './index.js'\nimport {createEffect} from 'solid-js'\n\nclass Foo {\n\t@signal foo = 123\n\n\t@signal get lorem() {\n\t\treturn 123\n\t}\n\t@signal set lorem(v) {\n\t\tv\n\t}\n}\n\nclass Bar extends Foo {\n\t// This causes a TDZ error if it comes after bar. See \"TDZ\" example in signal.test.ts.\n\t// Spec ordering issue: https://github.com/tc39/proposal-decorators/issues/571\n\t#baz = 789\n\n\t@signal bar = 456\n\n\t// This would cause a TDZ error.\n\t// #baz = 789\n\n\t@signal get baz() {\n\t\treturn this.#baz\n\t}\n\t@signal set baz(v) {\n\t\tthis.#baz = v\n\t}\n\n\t@effect logFoo() {\n\t\tconsole.log('this.foo:', this.foo)\n\t}\n\n\t@effect logLorem() {\n\t\tconsole.log('this.lorem:', this.lorem)\n\t}\n\n\t@effect logBar() {\n\t\tconsole.log('this.bar:', this.bar)\n\t}\n\n\t@effect logBaz() {\n\t\tconsole.log('this.baz:', this.baz)\n\t}\n}\n\nexport {Foo}\n\nconsole.log('---------')\nconst b = new Bar()\n\ncreateEffect(() => {\n\tconsole.log('b.foo:', b.foo)\n})\n\ncreateEffect(() => {\n\tconsole.log('b.lorem:', b.lorem)\n})\n\ncreateEffect(() => {\n\tconsole.log('b.bar:', b.bar)\n})\n\ncreateEffect(() => {\n\tconsole.log('b.baz:', b.baz)\n})\n\nsetInterval(() => {\n\tconsole.log('---')\n\tb.foo++\n\tb.bar++\n\tb.baz++\n\tb.lorem++\n}, 1000)\n"],"mappings":";;;;;;AAAA,SAAQA,MAAM,EAAEC,MAAM,QAAO,YAAY;AACzC,SAAQC,YAAY,QAAO,UAAU;AAErC,MAAMC,GAAG,CAAC;EAAA;IAAA,CAAAC,SAAA,EAAAC,eAAA,EAAAC,UAAA,IAAAC,UAAA,aACRP,MAAM,cAENA,MAAM,gBAGNA,MAAM,gBAAAQ,CAAA;EAAA;EAAAC,YAAA;IAAAJ,eAAA;EAAA;EALCK,GAAG,IAAAJ,UAAA,QAAAF,SAAA,OAAG,GAAG;EAEjB,IAAYO,KAAKA,CAAA,EAAG;IACnB,OAAO,GAAG;EACX;EACA,IAAYA,KAAKA,CAACC,CAAC,EAAE;IACpBA,CAAC;EACF;AACD;AAEA,MAAMC,GAAG,SAASV,GAAG,CAAC;EAAA;IAAA,CAAAW,SAAA,EAAAC,eAAA,EAAAC,WAAA,IAAAT,UAAA,aAKpBP,MAAM,cAKNA,MAAM,cAGNA,MAAM,cAINC,MAAM,iBAINA,MAAM,mBAINA,MAAM,iBAINA,MAAM,4BA7BUE,GAAG,EAAAK,CAAA;EAAA;EAAAC,YAAA,GAAAQ,IAAA;IAAA,SAAAA,IAAA;IAAAF,eAAA;EAAA;EACpB;EACA;EACA,CAACG,GAAG,IAAAF,WAAA,QAAG,GAAG;EAEFG,GAAG,GAAAL,SAAA,OAAG,GAAG;;EAEjB;EACA;;EAEA,IAAYI,GAAGA,CAAA,EAAG;IACjB,OAAO,IAAI,CAAC,CAACA,GAAG;EACjB;EACA,IAAYA,GAAGA,CAACN,CAAC,EAAE;IAClB,IAAI,CAAC,CAACM,GAAG,GAAGN,CAAC;EACd;EAEQQ,MAAMA,CAAA,EAAG;IAChBC,OAAO,CAACC,GAAG,CAAC,WAAW,EAAE,IAAI,CAACZ,GAAG,CAAC;EACnC;EAEQa,QAAQA,CAAA,EAAG;IAClBF,OAAO,CAACC,GAAG,CAAC,aAAa,EAAE,IAAI,CAACX,KAAK,CAAC;EACvC;EAEQa,MAAMA,CAAA,EAAG;IAChBH,OAAO,CAACC,GAAG,CAAC,WAAW,EAAE,IAAI,CAACH,GAAG,CAAC;EACnC;EAEQM,MAAMA,CAAA,EAAG;IAChBJ,OAAO,CAACC,GAAG,CAAC,WAAW,EAAE,IAAI,CAACJ,GAAG,CAAC;EACnC;AACD;AAEA,SAAQf,GAAG;AAEXkB,OAAO,CAACC,GAAG,CAAC,WAAW,CAAC;AACxB,MAAMI,CAAC,GAAG,IAAIb,GAAG,CAAC,CAAC;AAEnBX,YAAY,CAAC,MAAM;EAClBmB,OAAO,CAACC,GAAG,CAAC,QAAQ,EAAEI,CAAC,CAAChB,GAAG,CAAC;AAC7B,CAAC,CAAC;AAEFR,YAAY,CAAC,MAAM;EAClBmB,OAAO,CAACC,GAAG,CAAC,UAAU,EAAEI,CAAC,CAACf,KAAK,CAAC;AACjC,CAAC,CAAC;AAEFT,YAAY,CAAC,MAAM;EAClBmB,OAAO,CAACC,GAAG,CAAC,QAAQ,EAAEI,CAAC,CAACP,GAAG,CAAC;AAC7B,CAAC,CAAC;AAEFjB,YAAY,CAAC,MAAM;EAClBmB,OAAO,CAACC,GAAG,CAAC,QAAQ,EAAEI,CAAC,CAACR,GAAG,CAAC;AAC7B,CAAC,CAAC;AAEFS,WAAW,CAAC,MAAM;EACjBN,OAAO,CAACC,GAAG,CAAC,KAAK,CAAC;EAClBI,CAAC,CAAChB,GAAG,EAAE;EACPgB,CAAC,CAACP,GAAG,EAAE;EACPO,CAAC,CAACR,GAAG,EAAE;EACPQ,CAAC,CAACf,KAAK,EAAE;AACV,CAAC,EAAE,IAAI,CAAC","ignoreList":[]} \ No newline at end of file diff --git a/dist/index.test.d.ts b/dist/index.test.d.ts index ebeb90f..1794af0 100644 --- a/dist/index.test.d.ts +++ b/dist/index.test.d.ts @@ -5,4 +5,99 @@ export declare function testButterflyProps(b: { colors: number; wingSize: number; }, initialColors?: number): void; +declare const MyElement_base: { + new (...a: any[]): { + "__#1@#effectFunctions": (() => void)[]; + "__#1@#started": boolean; + createEffect(fn: () => void): void; + "__#1@#isRestarting": boolean; + startEffects(): void; + stopEffects(): void; + clearEffects(): void; + "__#1@#owner": import("solid-js").Owner | null; + "__#1@#dispose": (() => void) | null; + "__#1@#createEffect"(fn: () => void): void; + }; +} & { + new (): HTMLElement; + prototype: HTMLElement; +}; +export declare class MyElement extends MyElement_base { + a: number; + b: number; + runs: number; + result: number; + connectedCallback(): void; + disconnectedCallback(): void; +} +declare const MyElement2_base: { + new (...a: any[]): { + "__#1@#effectFunctions": (() => void)[]; + "__#1@#started": boolean; + createEffect(fn: () => void): void; + "__#1@#isRestarting": boolean; + startEffects(): void; + stopEffects(): void; + clearEffects(): void; + "__#1@#owner": import("solid-js").Owner | null; + "__#1@#dispose": (() => void) | null; + "__#1@#createEffect"(fn: () => void): void; + }; +} & { + new (): HTMLElement; + prototype: HTMLElement; +}; +export declare class MyElement2 extends MyElement2_base { + a: number; + b: number; + runs: number; + result: number; + constructor(); + connectedCallback(): void; + disconnectedCallback(): void; +} +declare const MyElement3_base: { + new (...a: any[]): { + "__#1@#effectFunctions": (() => void)[]; + "__#1@#started": boolean; + createEffect(fn: () => void): void; + "__#1@#isRestarting": boolean; + startEffects(): void; + stopEffects(): void; + clearEffects(): void; + "__#1@#owner": import("solid-js").Owner | null; + "__#1@#dispose": (() => void) | null; + "__#1@#createEffect"(fn: () => void): void; + }; +} & { + new (): HTMLElement; + prototype: HTMLElement; +}; +export declare class MyElement3 extends MyElement3_base { + runs: number; + result: number; + a: number; + b: number; + get sum(): number; + log(): void; + connectedCallback(): void; + disconnectedCallback(): void; +} +export declare class MyElement4 extends HTMLElement { + runs: number; + result: number; + a: number; + b: number; + get sum(): number; + log(): void; + connectedCallback(): void; + disconnectedCallback(): void; +} +export declare function testElementEffects(el: Element & { + a: number; + b: number; + result: number; + runs: number; +}): void; +export {}; //# sourceMappingURL=index.test.d.ts.map \ No newline at end of file diff --git a/dist/index.test.d.ts.map b/dist/index.test.d.ts.map index a1b60d4..d9e5760 100644 --- a/dist/index.test.d.ts.map +++ b/dist/index.test.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAIA,OAAO,CAAC,MAAM,CAAC;IACd,SAAS,MAAM,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAAA;CACpC;AAGD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,EAAE,aAAa,SAAI,QAwB1F"} \ No newline at end of file +{"version":3,"file":"index.test.d.ts","sourceRoot":"","sources":["../src/index.test.ts"],"names":[],"mappings":"AAMA,OAAO,CAAC,MAAM,CAAC;IACd,SAAS,MAAM,CAAC,GAAG,IAAI,EAAE,GAAG,EAAE,GAAG,GAAG,CAAA;CACpC;AAGD,wBAAgB,kBAAkB,CAAC,CAAC,EAAE;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAC,EAAE,aAAa,SAAI,QAwB1F;;;;;;;;;;;;;;;;;;AAED,qBAAa,SAAU,SAAQ,cAAsB;IAK5C,CAAC,SAAI;IACL,CAAC,SAAI;IAEb,IAAI,SAAI;IACR,MAAM,SAAI;IAEV,iBAAiB;IAOjB,oBAAoB;CAGpB;;;;;;;;;;;;;;;;;;AAED,qBAAa,UAAW,SAAQ,eAAsB;IAK7C,CAAC,SAAI;IACL,CAAC,SAAI;IAEb,IAAI,SAAI;IACR,MAAM,SAAI;;IAUV,iBAAiB;IAIjB,oBAAoB;CAGpB;;;;;;;;;;;;;;;;;;AAED,qBAAa,UAAW,SAAQ,eAAsB;IAKrD,IAAI,SAAI;IACR,MAAM,SAAI;IAEF,CAAC,SAAI;IACL,CAAC,SAAI;IAEb,IAAU,GAAG,WAEZ;IAEO,GAAG;IAKX,iBAAiB;IAIjB,oBAAoB;CAGpB;AACD,qBAAa,UAAW,SAAQ,WAAW;IAK1C,IAAI,SAAI;IACR,MAAM,SAAI;IAEF,CAAC,SAAI;IACL,CAAC,SAAI;IAEb,IAAU,GAAG,WAEZ;IAEO,GAAG;IAKX,iBAAiB;IAIjB,oBAAoB;CAGpB;AAED,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,OAAO,GAAG;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAC,QAoCpG"} \ No newline at end of file diff --git a/dist/index.test.js b/dist/index.test.js index 2438dc9..70e9c0e 100644 --- a/dist/index.test.js +++ b/dist/index.test.js @@ -1,4 +1,13 @@ +var _Effectful, _Effectful2, _Effectful3, _HTMLElement; +let _init_a, _init_extra_a, _init_b, _init_extra_b, _init_a2, _init_extra_a2, _init_b2, _init_extra_b2, _initProto, _init_a3, _init_extra_a3, _init_b3, _init_extra_b3, _initProto2, _init_a4, _init_extra_a4, _init_b4, _init_extra_b4; +function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } import { createComputed, createRoot } from 'solid-js'; +import { Effectful } from './mixins/index.js'; +import { effect, memo, signal, startEffects, stopEffects } from './decorators/index.js'; // TODO move type def to @lume/cli, map @types/jest's `expect` type into the // global env. @@ -23,4 +32,143 @@ export function testButterflyProps(b, initialColors = 3) { expect(b.wingSize).toBe(3, 'incremented wingSize value'); expect(count).toBe(3, 'Should be reactive'); } +export class MyElement extends (_Effectful = Effectful(HTMLElement)) { + static { + [_init_a, _init_extra_a, _init_b, _init_extra_b] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"]], 0, void 0, _Effectful).e; + } + static { + customElements.define('my-element', this); + } + a = _init_a(this, 1); + b = (_init_extra_a(this), _init_b(this, 2)); + runs = (_init_extra_b(this), 0); + result = 0; + connectedCallback() { + this.createEffect(() => { + this.runs++; + this.result = this.a + this.b; + }); + } + disconnectedCallback() { + this.clearEffects(); + } +} +export class MyElement2 extends (_Effectful2 = Effectful(HTMLElement)) { + static { + [_init_a2, _init_extra_a2, _init_b2, _init_extra_b2] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"]], 0, void 0, _Effectful2).e; + } + static { + customElements.define('my-element2', this); + } + a = _init_a2(this, 1); + b = (_init_extra_a2(this), _init_b2(this, 2)); + runs = (_init_extra_b2(this), 0); + result = 0; + constructor() { + super(); + this.createEffect(() => { + this.runs++; + this.result = this.a + this.b; + }); + } + connectedCallback() { + this.startEffects(); + } + disconnectedCallback() { + this.stopEffects(); + } +} +export class MyElement3 extends (_Effectful3 = Effectful(HTMLElement)) { + static { + [_init_a3, _init_extra_a3, _init_b3, _init_extra_b3, _initProto] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 3, "sum"], [effect, 2, "log"]], 0, void 0, _Effectful3).e; + } + constructor(...args) { + super(...args); + _init_extra_b3(this); + } + static { + customElements.define('my-element3', this); + } + runs = (_initProto(this), 0); + result = 0; + a = _init_a3(this, 1); + b = (_init_extra_a3(this), _init_b3(this, 2)); + get sum() { + return this.a + this.b; + } + log() { + this.runs++; + this.result = this.sum; + } + connectedCallback() { + this.startEffects(); + } + disconnectedCallback() { + this.stopEffects(); + } +} +export class MyElement4 extends (_HTMLElement = HTMLElement) { + static { + [_init_a4, _init_extra_a4, _init_b4, _init_extra_b4, _initProto2] = _applyDecs(this, [], [[signal, 0, "a"], [signal, 0, "b"], [memo, 3, "sum"], [effect, 2, "log"]], 0, void 0, _HTMLElement).e; + } + constructor(...args) { + super(...args); + _init_extra_b4(this); + } + static { + customElements.define('my-element4', this); + } + runs = (_initProto2(this), 0); + result = 0; + a = _init_a4(this, 1); + b = (_init_extra_a4(this), _init_b4(this, 2)); + get sum() { + return this.a + this.b; + } + log() { + this.runs++; + this.result = this.sum; + } + connectedCallback() { + startEffects(this); + } + disconnectedCallback() { + stopEffects(this); + } +} +export function testElementEffects(el) { + document.body.append(el); // triggers connectedCallback + + expect(el.result).toBe(1 + 2); + expect(el.runs).toBe(1); + el.a = 5; + expect(el.result).toBe(5 + 2); + expect(el.runs).toBe(2); + el.b = 10; + expect(el.result).toBe(5 + 10); + expect(el.runs).toBe(3); + + // disconnect the element + + document.body.removeChild(el); // triggers disconnectedCallback + + // Further signal changes do not affect result while disconnected + el.a = 20; + el.b = 30; + expect(el.result).toBe(5 + 10); + expect(el.runs).toBe(3); + + // reconnect the element + document.body.append(el); // triggers connectedCallback + expect(el.result).toBe(20 + 30); + expect(el.runs).toBe(4); + + // further signal changes work again + el.a = 100; + expect(el.result).toBe(100 + 30); + expect(el.runs).toBe(5); + el.b = 200; + expect(el.result).toBe(100 + 200); + expect(el.runs).toBe(6); +} //# sourceMappingURL=index.test.js.map \ No newline at end of file diff --git a/dist/index.test.js.map b/dist/index.test.js.map index 79a6bdb..5e6defb 100644 --- a/dist/index.test.js.map +++ b/dist/index.test.js.map @@ -1 +1 @@ -{"version":3,"file":"index.test.js","names":["createComputed","createRoot","testButterflyProps","b","initialColors","count","colors","wingSize","expect","toBe"],"sources":["../src/index.test.ts"],"sourcesContent":["import {createComputed, createRoot} from 'solid-js'\n\n// TODO move type def to @lume/cli, map @types/jest's `expect` type into the\n// global env.\ndeclare global {\n\tfunction expect(...args: any[]): any\n}\n\n// Test helper shared with other test files.\nexport function testButterflyProps(b: {colors: number; wingSize: number}, initialColors = 3) {\n\tlet count = 0\n\n\tcreateRoot(() => {\n\t\tcreateComputed(() => {\n\t\t\tb.colors\n\t\t\tb.wingSize\n\t\t\tcount++\n\t\t})\n\t})\n\n\texpect(b.colors).toBe(initialColors, 'initial colors value')\n\texpect(b.wingSize).toBe(2, 'initial wingSize value')\n\texpect(count).toBe(1, 'Should be reactive')\n\n\tb.colors++\n\n\texpect(b.colors).toBe(initialColors + 1, 'incremented colors value')\n\texpect(count).toBe(2, 'Should be reactive')\n\n\tb.wingSize++\n\n\texpect(b.wingSize).toBe(3, 'incremented wingSize value')\n\texpect(count).toBe(3, 'Should be reactive')\n}\n"],"mappings":"AAAA,SAAQA,cAAc,EAAEC,UAAU,QAAO,UAAU;;AAEnD;AACA;;AAKA;AACA,OAAO,SAASC,kBAAkBA,CAACC,CAAqC,EAAEC,aAAa,GAAG,CAAC,EAAE;EAC5F,IAAIC,KAAK,GAAG,CAAC;EAEbJ,UAAU,CAAC,MAAM;IAChBD,cAAc,CAAC,MAAM;MACpBG,CAAC,CAACG,MAAM;MACRH,CAAC,CAACI,QAAQ;MACVF,KAAK,EAAE;IACR,CAAC,CAAC;EACH,CAAC,CAAC;EAEFG,MAAM,CAACL,CAAC,CAACG,MAAM,CAAC,CAACG,IAAI,CAACL,aAAa,EAAE,sBAAsB,CAAC;EAC5DI,MAAM,CAACL,CAAC,CAACI,QAAQ,CAAC,CAACE,IAAI,CAAC,CAAC,EAAE,wBAAwB,CAAC;EACpDD,MAAM,CAACH,KAAK,CAAC,CAACI,IAAI,CAAC,CAAC,EAAE,oBAAoB,CAAC;EAE3CN,CAAC,CAACG,MAAM,EAAE;EAEVE,MAAM,CAACL,CAAC,CAACG,MAAM,CAAC,CAACG,IAAI,CAACL,aAAa,GAAG,CAAC,EAAE,0BAA0B,CAAC;EACpEI,MAAM,CAACH,KAAK,CAAC,CAACI,IAAI,CAAC,CAAC,EAAE,oBAAoB,CAAC;EAE3CN,CAAC,CAACI,QAAQ,EAAE;EAEZC,MAAM,CAACL,CAAC,CAACI,QAAQ,CAAC,CAACE,IAAI,CAAC,CAAC,EAAE,4BAA4B,CAAC;EACxDD,MAAM,CAACH,KAAK,CAAC,CAACI,IAAI,CAAC,CAAC,EAAE,oBAAoB,CAAC;AAC5C","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"index.test.js","names":["createComputed","createRoot","Effectful","effect","memo","signal","startEffects","stopEffects","testButterflyProps","b","initialColors","count","colors","wingSize","expect","toBe","MyElement","_Effectful","HTMLElement","_init_a","_init_extra_a","_init_b","_init_extra_b","_applyDecs","e","customElements","define","a","runs","result","connectedCallback","createEffect","disconnectedCallback","clearEffects","MyElement2","_Effectful2","_init_a2","_init_extra_a2","_init_b2","_init_extra_b2","constructor","MyElement3","_Effectful3","_init_a3","_init_extra_a3","_init_b3","_init_extra_b3","_initProto","args","sum","log","MyElement4","_HTMLElement","_init_a4","_init_extra_a4","_init_b4","_init_extra_b4","_initProto2","testElementEffects","el","document","body","append","removeChild"],"sources":["../src/index.test.ts"],"sourcesContent":["import {createComputed, createRoot} from 'solid-js'\nimport {Effectful} from './mixins/index.js'\nimport {effect, memo, signal, startEffects, stopEffects} from './decorators/index.js'\n\n// TODO move type def to @lume/cli, map @types/jest's `expect` type into the\n// global env.\ndeclare global {\n\tfunction expect(...args: any[]): any\n}\n\n// Test helper shared with other test files.\nexport function testButterflyProps(b: {colors: number; wingSize: number}, initialColors = 3) {\n\tlet count = 0\n\n\tcreateRoot(() => {\n\t\tcreateComputed(() => {\n\t\t\tb.colors\n\t\t\tb.wingSize\n\t\t\tcount++\n\t\t})\n\t})\n\n\texpect(b.colors).toBe(initialColors, 'initial colors value')\n\texpect(b.wingSize).toBe(2, 'initial wingSize value')\n\texpect(count).toBe(1, 'Should be reactive')\n\n\tb.colors++\n\n\texpect(b.colors).toBe(initialColors + 1, 'incremented colors value')\n\texpect(count).toBe(2, 'Should be reactive')\n\n\tb.wingSize++\n\n\texpect(b.wingSize).toBe(3, 'incremented wingSize value')\n\texpect(count).toBe(3, 'Should be reactive')\n}\n\nexport class MyElement extends Effectful(HTMLElement) {\n\tstatic {\n\t\tcustomElements.define('my-element', this)\n\t}\n\n\t@signal a = 1\n\t@signal b = 2\n\n\truns = 0\n\tresult = 0\n\n\tconnectedCallback() {\n\t\tthis.createEffect(() => {\n\t\t\tthis.runs++\n\t\t\tthis.result = this.a + this.b\n\t\t})\n\t}\n\n\tdisconnectedCallback() {\n\t\tthis.clearEffects()\n\t}\n}\n\nexport class MyElement2 extends Effectful(HTMLElement) {\n\tstatic {\n\t\tcustomElements.define('my-element2', this)\n\t}\n\n\t@signal a = 1\n\t@signal b = 2\n\n\truns = 0\n\tresult = 0\n\n\tconstructor() {\n\t\tsuper()\n\t\tthis.createEffect(() => {\n\t\t\tthis.runs++\n\t\t\tthis.result = this.a + this.b\n\t\t})\n\t}\n\n\tconnectedCallback() {\n\t\tthis.startEffects()\n\t}\n\n\tdisconnectedCallback() {\n\t\tthis.stopEffects()\n\t}\n}\n\nexport class MyElement3 extends Effectful(HTMLElement) {\n\tstatic {\n\t\tcustomElements.define('my-element3', this)\n\t}\n\n\truns = 0\n\tresult = 0\n\n\t@signal a = 1\n\t@signal b = 2\n\n\t@memo get sum() {\n\t\treturn this.a + this.b\n\t}\n\n\t@effect log() {\n\t\tthis.runs++\n\t\tthis.result = this.sum\n\t}\n\n\tconnectedCallback() {\n\t\tthis.startEffects()\n\t}\n\n\tdisconnectedCallback() {\n\t\tthis.stopEffects()\n\t}\n}\nexport class MyElement4 extends HTMLElement {\n\tstatic {\n\t\tcustomElements.define('my-element4', this)\n\t}\n\n\truns = 0\n\tresult = 0\n\n\t@signal a = 1\n\t@signal b = 2\n\n\t@memo get sum() {\n\t\treturn this.a + this.b\n\t}\n\n\t@effect log() {\n\t\tthis.runs++\n\t\tthis.result = this.sum\n\t}\n\n\tconnectedCallback() {\n\t\tstartEffects(this)\n\t}\n\n\tdisconnectedCallback() {\n\t\tstopEffects(this)\n\t}\n}\n\nexport function testElementEffects(el: Element & {a: number; b: number; result: number; runs: number}) {\n\tdocument.body.append(el) // triggers connectedCallback\n\n\texpect(el.result).toBe(1 + 2)\n\texpect(el.runs).toBe(1)\n\n\tel.a = 5\n\texpect(el.result).toBe(5 + 2)\n\texpect(el.runs).toBe(2)\n\n\tel.b = 10\n\texpect(el.result).toBe(5 + 10)\n\texpect(el.runs).toBe(3)\n\n\t// disconnect the element\n\n\tdocument.body.removeChild(el) // triggers disconnectedCallback\n\n\t// Further signal changes do not affect result while disconnected\n\tel.a = 20\n\tel.b = 30\n\texpect(el.result).toBe(5 + 10)\n\texpect(el.runs).toBe(3)\n\n\t// reconnect the element\n\tdocument.body.append(el) // triggers connectedCallback\n\texpect(el.result).toBe(20 + 30)\n\texpect(el.runs).toBe(4)\n\n\t// further signal changes work again\n\tel.a = 100\n\texpect(el.result).toBe(100 + 30)\n\texpect(el.runs).toBe(5)\n\tel.b = 200\n\texpect(el.result).toBe(100 + 200)\n\texpect(el.runs).toBe(6)\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/mixins/Effectful.d.ts b/dist/mixins/Effectful.d.ts index d151dbb..7ed730c 100644 --- a/dist/mixins/Effectful.d.ts +++ b/dist/mixins/Effectful.d.ts @@ -1,6 +1,5 @@ import { type Owner } from 'solid-js'; import type { AnyConstructor } from 'lowclass/dist/Constructor.js'; -import { type Effect } from '../effects/createStoppableEffect.js'; /** * @class Effectful - * @@ -12,22 +11,24 @@ import { type Effect } from '../effects/createStoppableEffect.js'; * Example: * * ```js - * import {reactive, signal} from 'classy-solid' + * import {signal} from 'classy-solid' * import {foo} from 'somewhere' - * import {bar} from 'elsewhere' + * import {BaseClass} from 'some-lib' * * class MyClass extends Effectful(BaseClass) { + * @signal bar = 0 + * * constructor() { * super() * * // Log `foo` and `bar` any time either of them change. * this.createEffect(() => { - * console.log('foo, bar:', foo(), bar()) + * console.log('foo, bar:', foo(), this.bar) * }) * * // Log only `bar` any time it changes. * this.createEffect(() => { - * console.log('bar:', bar()) + * console.log('bar:', this.bar) * }) * } * @@ -37,73 +38,181 @@ import { type Effect } from '../effects/createStoppableEffect.js'; * } * } * ``` + * + * This pairs nicely with the `@effect` decorator. The previous example could be + * rewritten as: + * + * ```js + * import {signal, effect} from 'classy-solid' + * import {foo} from 'somewhere' + * import {BaseClass} from 'some-lib' + * + * class MyClass extends Effectful(BaseClass) { + * @signal bar = 0 + * + * @effect logFooBar() { + * console.log('foo, bar:', foo(), this.bar) + * } + * + * @effect logBar() { + * console.log('bar:', this.bar) + * } + * + * dispose() { + * // Later, stop both of the effects. + * this.stopEffects() + * } + * } + * ``` */ export declare function Effectful(Base: T): { new (...a: any[]): { - "__#1@#effects": Set; + "__#1@#effectFunctions": Array<() => void>; + "__#1@#started": boolean; /** * Create a Solid.js effect. The difference from regular * `createEffect()` is that `this` tracks the effects created, so that * they can all be stopped with `this.stopEffects()`. + */ + createEffect(fn: () => void): void; + "__#1@#isRestarting": boolean; + /** + * Start all effects again. This will recreate all effects that were + * previously created with `createEffect()` and stopped with `stopEffects()`. + * + * Example with a custom element class using the @effect decorator: * - * Effects can also be stopped or resumed individually: + * ```ts + * const [someSignal, setSomeSignal] = createSignal(0) * - * ```js - * const effect1 = this.createEffect(() => {...}) - * const effect2 = this.createEffect(() => {...}) + * class MyElement extends Effectful(HTMLElement) { + * @effect logSignal() { + * console.log('someSignal:', someSignal()) + * } * - * // ...later - * effect1.stop() + * connectedCallback() { + * this.startEffects() + * } * - * // ...later - * effect1.resume() + * disconnectedCallback() { + * this.stopEffects() + * } + * } * ``` + * + * The logging of `someSignal` will happen any time `someSignal` changes + * only while the element is connected, and but not when it is + * disconnected. */ - createEffect(fn: () => void): void; + startEffects(): void; /** * Stop all of the effects that were created. */ stopEffects(): void; - "__#1@#createEffect1"(fn: () => void): void; - "__#1@#stopEffects1"(): void; + /** + * Stop all effects and clear the stored effect functions. After calling + * this, `startEffects()` will not restart any effects because there are + * none stored. This is useful for cleanup scenarios where you'll make + * new effects using `this.createEffect()` instead of restarting old + * ones, namely for backwards compatibility for example with custom + * elements that may be disconnected and reconnected to the DOM and + * currently call `this.createEffect()` in connectedCallback. Example: + * + * ```ts + * class MyElement extends Effectful(HTMLElement) { + * connectedCallback() { + * // Create any number of effects on connect. + * this.createEffect(() => {...}) + * this.createEffect(() => {...}) + * this.createEffect(() => {...}) + * } + * + * disconnectedCallback() { + * // Clean up all effects on disconnect. + * this.clearEffects() + * } + * } + * ``` + */ + clearEffects(): void; "__#1@#owner": Owner | null; "__#1@#dispose": (() => void) | null; - "__#1@#createEffect2"(fn: () => void): void; - "__#1@#stopEffects2"(): void; + "__#1@#createEffect"(fn: () => void): void; }; } & T; declare const Effects_base: { new (...a: any[]): { - "__#1@#effects": Set; + "__#1@#effectFunctions": (() => void)[]; + "__#1@#started": boolean; /** * Create a Solid.js effect. The difference from regular * `createEffect()` is that `this` tracks the effects created, so that * they can all be stopped with `this.stopEffects()`. + */ + createEffect(fn: () => void): void; + "__#1@#isRestarting": boolean; + /** + * Start all effects again. This will recreate all effects that were + * previously created with `createEffect()` and stopped with `stopEffects()`. + * + * Example with a custom element class using the @effect decorator: * - * Effects can also be stopped or resumed individually: + * ```ts + * const [someSignal, setSomeSignal] = createSignal(0) * - * ```js - * const effect1 = this.createEffect(() => {...}) - * const effect2 = this.createEffect(() => {...}) + * class MyElement extends Effectful(HTMLElement) { + * @effect logSignal() { + * console.log('someSignal:', someSignal()) + * } * - * // ...later - * effect1.stop() + * connectedCallback() { + * this.startEffects() + * } * - * // ...later - * effect1.resume() + * disconnectedCallback() { + * this.stopEffects() + * } + * } * ``` + * + * The logging of `someSignal` will happen any time `someSignal` changes + * only while the element is connected, and but not when it is + * disconnected. */ - createEffect(fn: () => void): void; + startEffects(): void; /** * Stop all of the effects that were created. */ stopEffects(): void; - "__#1@#createEffect1"(fn: () => void): void; - "__#1@#stopEffects1"(): void; + /** + * Stop all effects and clear the stored effect functions. After calling + * this, `startEffects()` will not restart any effects because there are + * none stored. This is useful for cleanup scenarios where you'll make + * new effects using `this.createEffect()` instead of restarting old + * ones, namely for backwards compatibility for example with custom + * elements that may be disconnected and reconnected to the DOM and + * currently call `this.createEffect()` in connectedCallback. Example: + * + * ```ts + * class MyElement extends Effectful(HTMLElement) { + * connectedCallback() { + * // Create any number of effects on connect. + * this.createEffect(() => {...}) + * this.createEffect(() => {...}) + * this.createEffect(() => {...}) + * } + * + * disconnectedCallback() { + * // Clean up all effects on disconnect. + * this.clearEffects() + * } + * } + * ``` + */ + clearEffects(): void; "__#1@#owner": Owner | null; "__#1@#dispose": (() => void) | null; - "__#1@#createEffect2"(fn: () => void): void; - "__#1@#stopEffects2"(): void; + "__#1@#createEffect"(fn: () => void): void; }; } & { new (): {}; diff --git a/dist/mixins/Effectful.d.ts.map b/dist/mixins/Effectful.d.ts.map index db39cb1..4fb0659 100644 --- a/dist/mixins/Effectful.d.ts.map +++ b/dist/mixins/Effectful.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"Effectful.d.ts","sourceRoot":"","sources":["../../src/mixins/Effectful.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,KAAK,EAA8D,MAAM,UAAU,CAAA;AAChG,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAChE,OAAO,EAAwB,KAAK,MAAM,EAAC,MAAM,qCAAqC,CAAA;AAEtF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,cAAc,EAAE,IAAI,EAAE,CAAC;;;QAIzD;;;;;;;;;;;;;;;;;WAiBG;yBACc,MAAM,IAAI;QAQ3B;;WAEG;;kCAUgB,MAAM,IAAI;;uBAoBrB,KAAK,GAAG,IAAI;yBACV,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI;kCAEV,MAAM,IAAI;;;MA0B9B;;;;QAvFC;;;;;;;;;;;;;;;;;WAiBG;+BACoB,IAAI;QAQ3B;;WAEG;;wCAUsB,IAAI;;uBAoBrB,KAAK,GAAG,IAAI;gCACH,IAAI;wCAEI,IAAI;;;;;;AA4B/B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,OAAQ,SAAQ,YAAmB;CAAG"} \ No newline at end of file +{"version":3,"file":"Effectful.d.ts","sourceRoot":"","sources":["../../src/mixins/Effectful.ts"],"names":[],"mappings":"AAAA,OAAO,EAAC,KAAK,KAAK,EAAmD,MAAM,UAAU,CAAA;AACrF,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAIhE;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgEG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,cAAc,EAAE,IAAI,EAAE,CAAC;;iCAKvC,KAAK,CAAC,MAAM,IAAI,CAAC;;QAGnC;;;;WAIG;yBACc,MAAM,IAAI;;QAO3B;;;;;;;;;;;;;;;;;;;;;;;;;;;WA2BG;;QAcH;;WAEG;;QAQH;;;;;;;;;;;;;;;;;;;;;;;;WAwBG;;uBAMK,KAAK,GAAG,IAAI;yBACV,CAAC,MAAM,IAAI,CAAC,GAAG,IAAI;iCAEX,MAAM,IAAI;;MAgC7B;;;wCAnI+B,IAAI;;QAGlC;;;;WAIG;+BACoB,IAAI;;QAO3B;;;;;;;;;;;;;;;;;;;;;;;;;;;WA2BG;;QAcH;;WAEG;;QAQH;;;;;;;;;;;;;;;;;;;;;;;;WAwBG;;uBAMK,KAAK,GAAG,IAAI;gCACH,IAAI;uCAEG,IAAI;;;;;AAyC9B;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,qBAAa,OAAQ,SAAQ,YAAmB;CAAG"} \ No newline at end of file diff --git a/dist/mixins/Effectful.js b/dist/mixins/Effectful.js index 62a6619..0e6f267 100644 --- a/dist/mixins/Effectful.js +++ b/dist/mixins/Effectful.js @@ -1,5 +1,5 @@ -import { createEffect, onCleanup, createRoot, getOwner, runWithOwner } from 'solid-js'; -import { createStoppableEffect } from '../effects/createStoppableEffect.js'; +import { createEffect, createRoot, getOwner, runWithOwner } from 'solid-js'; +const isInstance = Symbol(); /** * @class Effectful - @@ -12,22 +12,24 @@ import { createStoppableEffect } from '../effects/createStoppableEffect.js'; * Example: * * ```js - * import {reactive, signal} from 'classy-solid' + * import {signal} from 'classy-solid' * import {foo} from 'somewhere' - * import {bar} from 'elsewhere' + * import {BaseClass} from 'some-lib' * * class MyClass extends Effectful(BaseClass) { + * @signal bar = 0 + * * constructor() { * super() * * // Log `foo` and `bar` any time either of them change. * this.createEffect(() => { - * console.log('foo, bar:', foo(), bar()) + * console.log('foo, bar:', foo(), this.bar) * }) * * // Log only `bar` any time it changes. * this.createEffect(() => { - * console.log('bar:', bar()) + * console.log('bar:', this.bar) * }) * } * @@ -37,93 +39,169 @@ import { createStoppableEffect } from '../effects/createStoppableEffect.js'; * } * } * ``` + * + * This pairs nicely with the `@effect` decorator. The previous example could be + * rewritten as: + * + * ```js + * import {signal, effect} from 'classy-solid' + * import {foo} from 'somewhere' + * import {BaseClass} from 'some-lib' + * + * class MyClass extends Effectful(BaseClass) { + * @signal bar = 0 + * + * @effect logFooBar() { + * console.log('foo, bar:', foo(), this.bar) + * } + * + * @effect logBar() { + * console.log('bar:', this.bar) + * } + * + * dispose() { + * // Later, stop both of the effects. + * this.stopEffects() + * } + * } + * ``` */ export function Effectful(Base) { - return class Effectful extends Base { - #effects = new Set(); + if (Base.prototype instanceof Effectful) throw new Error('Class already extends Effectful, no need to apply the mixin again.'); + class EffectfulClass extends Base { + #effectFunctions = []; + #started = true; /** * Create a Solid.js effect. The difference from regular * `createEffect()` is that `this` tracks the effects created, so that * they can all be stopped with `this.stopEffects()`. + */ + createEffect(fn) { + this.startEffects(); + this.#createEffect(fn); + } + #isRestarting = false; + + /** + * Start all effects again. This will recreate all effects that were + * previously created with `createEffect()` and stopped with `stopEffects()`. * - * Effects can also be stopped or resumed individually: + * Example with a custom element class using the @effect decorator: * - * ```js - * const effect1 = this.createEffect(() => {...}) - * const effect2 = this.createEffect(() => {...}) + * ```ts + * const [someSignal, setSomeSignal] = createSignal(0) * - * // ...later - * effect1.stop() + * class MyElement extends Effectful(HTMLElement) { + * @effect logSignal() { + * console.log('someSignal:', someSignal()) + * } * - * // ...later - * effect1.resume() + * connectedCallback() { + * this.startEffects() + * } + * + * disconnectedCallback() { + * this.stopEffects() + * } + * } * ``` + * + * The logging of `someSignal` will happen any time `someSignal` changes + * only while the element is connected, and but not when it is + * disconnected. */ - createEffect(fn) { - let method = 4; - if (method === 1) this.#createEffect1(fn); // not working, bugs out when inside a Solid render() root, effects stop re-running. https://discord.com/channels/722131463138705510/751355413701591120/1188246668466991134 - if (method === 2) createRoot(() => this.#createEffect1(fn)); // works without nesting, but leaks stopped effects until the parent owner is cleaned up (will never clean up if the parent is running for lifetime of the app). - if (method === 3) queueMicrotask(() => this.#createEffect1(fn)); // works without nesting, without leaks - if (method === 4) this.#createEffect2(fn); // works with nesting, without leaks + startEffects() { + if (this.#started) return; + this.#started = true; + + // Restart all stored effect functions + this.#isRestarting = true; + try { + for (const fn of this.#effectFunctions) this.#createEffect(fn); + } finally { + this.#isRestarting = false; + } } /** * Stop all of the effects that were created. */ stopEffects() { - let method = 2; - if (method === 1) this.#stopEffects1(); - if (method === 2) this.#stopEffects2(); + if (!this.#started) return; + this.#started = false; + this.#dispose?.(); } - // Method 1 ////////////////////////////////////////// - // Works fine when not in a parent context, or else currently leaks or has the above mentioned bug while a parent exists. - - #createEffect1(fn) { - let effect = null; - effect = createStoppableEffect(() => { - if (effect) this.#effects.add(effect); - // nest the user's effect so that if it re-runs a lot it is not deleting/adding from/to our #effects Set a lot. - createEffect(fn); - onCleanup(() => this.#effects.delete(effect)); - }); - this.#effects.add(effect); - } - #stopEffects1() { - for (const effect of this.#effects) effect.stop(); + /** + * Stop all effects and clear the stored effect functions. After calling + * this, `startEffects()` will not restart any effects because there are + * none stored. This is useful for cleanup scenarios where you'll make + * new effects using `this.createEffect()` instead of restarting old + * ones, namely for backwards compatibility for example with custom + * elements that may be disconnected and reconnected to the DOM and + * currently call `this.createEffect()` in connectedCallback. Example: + * + * ```ts + * class MyElement extends Effectful(HTMLElement) { + * connectedCallback() { + * // Create any number of effects on connect. + * this.createEffect(() => {...}) + * this.createEffect(() => {...}) + * this.createEffect(() => {...}) + * } + * + * disconnectedCallback() { + * // Clean up all effects on disconnect. + * this.clearEffects() + * } + * } + * ``` + */ + clearEffects() { + this.stopEffects(); + this.#effectFunctions = []; } - - // Method 2 ////////////////////////////////////////// - // Works, with nesting, no leaks. - #owner = null; #dispose = null; - #createEffect2(fn) { - if (!this.#owner) { + #createEffect(fn) { + const owner = getOwner(); + + // If nested in an existing owner (f.e. nested effect), delegate to + // regular createEffect. + if (owner) return createEffect(fn); + + // Store top-level effect functions so they can be replayed when + // startEffects() is called + if (!this.#isRestarting) this.#effectFunctions.push(fn); + + // If top-level call either attach to an existing root, or make a + // new one if we don't have one yet. + if (this.#owner) runWithOwner(this.#owner, () => createEffect(fn));else { createRoot(dispose => { this.#owner = getOwner(); - this.#dispose = dispose; - this.#createEffect2(fn); + this.#dispose = () => { + dispose(); + this.#owner = null; + }; + createEffect(fn); }); - } else { - let owner = getOwner(); - while (owner && owner !== this.#owner) owner = owner?.owner ?? null; - - // this.#owner found in the parents of current owner therefore, - // run with current nested owner like a regular solid - // createEffect() - if (owner === this.#owner) return createEffect(fn); - - // this.#owner wasn't found on the parent owners - // run with this.#owner - runWithOwner(this.#owner, () => createEffect(fn)); } } - #stopEffects2() { - this.#dispose?.(); - } - }; + } + ; + EffectfulClass.prototype[isInstance] = true; + Object.defineProperty(EffectfulClass, Symbol.hasInstance, { + value: instanceCheck + }); + return EffectfulClass; +} +Object.defineProperty(Effectful, Symbol.hasInstance, { + value: instanceCheck +}); +function instanceCheck(obj) { + if (!obj || typeof obj !== 'object') return false; + return isInstance in obj; } /** diff --git a/dist/mixins/Effectful.js.map b/dist/mixins/Effectful.js.map index 35dabcf..4a5a1f1 100644 --- a/dist/mixins/Effectful.js.map +++ b/dist/mixins/Effectful.js.map @@ -1 +1 @@ -{"version":3,"file":"Effectful.js","names":["createEffect","onCleanup","createRoot","getOwner","runWithOwner","createStoppableEffect","Effectful","Base","effects","Set","fn","method","createEffect1","queueMicrotask","createEffect2","stopEffects","stopEffects1","stopEffects2","#createEffect1","effect","add","delete","#stopEffects1","stop","owner","dispose","#createEffect2","#stopEffects2","Effects"],"sources":["../../src/mixins/Effectful.ts"],"sourcesContent":["import {type Owner, createEffect, onCleanup, createRoot, getOwner, runWithOwner} from 'solid-js'\nimport type {AnyConstructor} from 'lowclass/dist/Constructor.js'\nimport {createStoppableEffect, type Effect} from '../effects/createStoppableEffect.js'\n\n/**\n * @class Effectful -\n *\n * `mixin`\n *\n * Create Solid.js effects using `this.createEffect(fn)` and easily stop them\n * all by calling `this.stopEffects()`.\n *\n * Example:\n *\n * ```js\n * import {reactive, signal} from 'classy-solid'\n * import {foo} from 'somewhere'\n * import {bar} from 'elsewhere'\n *\n * class MyClass extends Effectful(BaseClass) {\n * constructor() {\n * super()\n *\n * // Log `foo` and `bar` any time either of them change.\n * this.createEffect(() => {\n * console.log('foo, bar:', foo(), bar())\n * })\n *\n * // Log only `bar` any time it changes.\n * this.createEffect(() => {\n * console.log('bar:', bar())\n * })\n * }\n *\n * dispose() {\n * // Later, stop both of the effects.\n * this.stopEffects()\n * }\n * }\n * ```\n */\nexport function Effectful(Base: T) {\n\treturn class Effectful extends Base {\n\t\t#effects = new Set()\n\n\t\t/**\n\t\t * Create a Solid.js effect. The difference from regular\n\t\t * `createEffect()` is that `this` tracks the effects created, so that\n\t\t * they can all be stopped with `this.stopEffects()`.\n\t\t *\n\t\t * Effects can also be stopped or resumed individually:\n\t\t *\n\t\t * ```js\n\t\t * const effect1 = this.createEffect(() => {...})\n\t\t * const effect2 = this.createEffect(() => {...})\n\t\t *\n\t\t * // ...later\n\t\t * effect1.stop()\n\t\t *\n\t\t * // ...later\n\t\t * effect1.resume()\n\t\t * ```\n\t\t */\n\t\tcreateEffect(fn: () => void) {\n\t\t\tlet method = 4\n\t\t\tif (method === 1) this.#createEffect1(fn) // not working, bugs out when inside a Solid render() root, effects stop re-running. https://discord.com/channels/722131463138705510/751355413701591120/1188246668466991134\n\t\t\tif (method === 2) createRoot(() => this.#createEffect1(fn)) // works without nesting, but leaks stopped effects until the parent owner is cleaned up (will never clean up if the parent is running for lifetime of the app).\n\t\t\tif (method === 3) queueMicrotask(() => this.#createEffect1(fn)) // works without nesting, without leaks\n\t\t\tif (method === 4) this.#createEffect2(fn) // works with nesting, without leaks\n\t\t}\n\n\t\t/**\n\t\t * Stop all of the effects that were created.\n\t\t */\n\t\tstopEffects() {\n\t\t\tlet method = 2\n\t\t\tif (method === 1) this.#stopEffects1()\n\t\t\tif (method === 2) this.#stopEffects2()\n\t\t}\n\n\t\t// Method 1 //////////////////////////////////////////\n\t\t// Works fine when not in a parent context, or else currently leaks or has the above mentioned bug while a parent exists.\n\n\t\t#createEffect1(fn: () => void) {\n\t\t\tlet effect: Effect | null = null\n\n\t\t\teffect = createStoppableEffect(() => {\n\t\t\t\tif (effect) this.#effects.add(effect)\n\t\t\t\t// nest the user's effect so that if it re-runs a lot it is not deleting/adding from/to our #effects Set a lot.\n\t\t\t\tcreateEffect(fn)\n\t\t\t\tonCleanup(() => this.#effects.delete(effect!))\n\t\t\t})\n\n\t\t\tthis.#effects.add(effect)\n\t\t}\n\n\t\t#stopEffects1() {\n\t\t\tfor (const effect of this.#effects) effect.stop()\n\t\t}\n\n\t\t// Method 2 //////////////////////////////////////////\n\t\t// Works, with nesting, no leaks.\n\n\t\t#owner: Owner | null = null\n\t\t#dispose: (() => void) | null = null\n\n\t\t#createEffect2(fn: () => void) {\n\t\t\tif (!this.#owner) {\n\t\t\t\tcreateRoot(dispose => {\n\t\t\t\t\tthis.#owner = getOwner()\n\t\t\t\t\tthis.#dispose = dispose\n\t\t\t\t\tthis.#createEffect2(fn)\n\t\t\t\t})\n\t\t\t} else {\n\t\t\t\tlet owner = getOwner()\n\t\t\t\twhile (owner && owner !== this.#owner) owner = owner?.owner ?? null\n\n\t\t\t\t// this.#owner found in the parents of current owner therefore,\n\t\t\t\t// run with current nested owner like a regular solid\n\t\t\t\t// createEffect()\n\t\t\t\tif (owner === this.#owner) return createEffect(fn)\n\n\t\t\t\t// this.#owner wasn't found on the parent owners\n\t\t\t\t// run with this.#owner\n\t\t\t\trunWithOwner(this.#owner, () => createEffect(fn))\n\t\t\t}\n\t\t}\n\n\t\t#stopEffects2() {\n\t\t\tthis.#dispose?.()\n\t\t}\n\t}\n}\n\n/**\n * Shortcut for instantiating or extending directly instead of using the mixin.\n * F.e.\n *\n * ```js\n * class Car extends Effects {\n * start() {\n * this.createEffect(() => {...})\n * this.createEffect(() => {...})\n * }\n * stop() {\n * this.stopEffects()\n * }\n * }\n *\n * const specialEffects = new Effects()\n * specialEffects.createEffect(() => {})\n * // ...later\n * specialEffects.stopEffects()\n * ```\n */\nexport class Effects extends Effectful(class {}) {}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"Effectful.js","names":["createEffect","createRoot","getOwner","runWithOwner","isInstance","Symbol","Effectful","Base","prototype","Error","EffectfulClass","effectFunctions","started","fn","startEffects","isRestarting","stopEffects","dispose","clearEffects","owner","#createEffect","push","Object","defineProperty","hasInstance","value","instanceCheck","obj","Effects"],"sources":["../../src/mixins/Effectful.ts"],"sourcesContent":["import {type Owner, createEffect, createRoot, getOwner, runWithOwner} from 'solid-js'\nimport type {AnyConstructor} from 'lowclass/dist/Constructor.js'\n\nconst isInstance = Symbol()\n\n/**\n * @class Effectful -\n *\n * `mixin`\n *\n * Create Solid.js effects using `this.createEffect(fn)` and easily stop them\n * all by calling `this.stopEffects()`.\n *\n * Example:\n *\n * ```js\n * import {signal} from 'classy-solid'\n * import {foo} from 'somewhere'\n * import {BaseClass} from 'some-lib'\n *\n * class MyClass extends Effectful(BaseClass) {\n * @signal bar = 0\n *\n * constructor() {\n * super()\n *\n * // Log `foo` and `bar` any time either of them change.\n * this.createEffect(() => {\n * console.log('foo, bar:', foo(), this.bar)\n * })\n *\n * // Log only `bar` any time it changes.\n * this.createEffect(() => {\n * console.log('bar:', this.bar)\n * })\n * }\n *\n * dispose() {\n * // Later, stop both of the effects.\n * this.stopEffects()\n * }\n * }\n * ```\n *\n * This pairs nicely with the `@effect` decorator. The previous example could be\n * rewritten as:\n *\n * ```js\n * import {signal, effect} from 'classy-solid'\n * import {foo} from 'somewhere'\n * import {BaseClass} from 'some-lib'\n *\n * class MyClass extends Effectful(BaseClass) {\n * @signal bar = 0\n *\n * @effect logFooBar() {\n * console.log('foo, bar:', foo(), this.bar)\n * }\n *\n * @effect logBar() {\n * console.log('bar:', this.bar)\n * }\n *\n * dispose() {\n * // Later, stop both of the effects.\n * this.stopEffects()\n * }\n * }\n * ```\n */\nexport function Effectful(Base: T) {\n\tif (Base.prototype instanceof Effectful)\n\t\tthrow new Error('Class already extends Effectful, no need to apply the mixin again.')\n\n\tclass EffectfulClass extends Base {\n\t\t#effectFunctions: Array<() => void> = []\n\t\t#started = true\n\n\t\t/**\n\t\t * Create a Solid.js effect. The difference from regular\n\t\t * `createEffect()` is that `this` tracks the effects created, so that\n\t\t * they can all be stopped with `this.stopEffects()`.\n\t\t */\n\t\tcreateEffect(fn: () => void) {\n\t\t\tthis.startEffects()\n\t\t\tthis.#createEffect(fn)\n\t\t}\n\n\t\t#isRestarting = false\n\n\t\t/**\n\t\t * Start all effects again. This will recreate all effects that were\n\t\t * previously created with `createEffect()` and stopped with `stopEffects()`.\n\t\t *\n\t\t * Example with a custom element class using the @effect decorator:\n\t\t *\n\t\t * ```ts\n\t\t * const [someSignal, setSomeSignal] = createSignal(0)\n\t\t *\n\t\t * class MyElement extends Effectful(HTMLElement) {\n\t\t * @effect logSignal() {\n\t\t * console.log('someSignal:', someSignal())\n\t\t * }\n\t\t *\n\t\t * connectedCallback() {\n\t\t * this.startEffects()\n\t\t * }\n\t\t *\n\t\t * disconnectedCallback() {\n\t\t * this.stopEffects()\n\t\t * }\n\t\t * }\n\t\t * ```\n\t\t *\n\t\t * The logging of `someSignal` will happen any time `someSignal` changes\n\t\t * only while the element is connected, and but not when it is\n\t\t * disconnected.\n\t\t */\n\t\tstartEffects() {\n\t\t\tif (this.#started) return\n\t\t\tthis.#started = true\n\n\t\t\t// Restart all stored effect functions\n\t\t\tthis.#isRestarting = true\n\t\t\ttry {\n\t\t\t\tfor (const fn of this.#effectFunctions) this.#createEffect(fn)\n\t\t\t} finally {\n\t\t\t\tthis.#isRestarting = false\n\t\t\t}\n\t\t}\n\n\t\t/**\n\t\t * Stop all of the effects that were created.\n\t\t */\n\t\tstopEffects() {\n\t\t\tif (!this.#started) return\n\t\t\tthis.#started = false\n\n\t\t\tthis.#dispose?.()\n\t\t}\n\n\t\t/**\n\t\t * Stop all effects and clear the stored effect functions. After calling\n\t\t * this, `startEffects()` will not restart any effects because there are\n\t\t * none stored. This is useful for cleanup scenarios where you'll make\n\t\t * new effects using `this.createEffect()` instead of restarting old\n\t\t * ones, namely for backwards compatibility for example with custom\n\t\t * elements that may be disconnected and reconnected to the DOM and\n\t\t * currently call `this.createEffect()` in connectedCallback. Example:\n\t\t *\n\t\t * ```ts\n\t\t * class MyElement extends Effectful(HTMLElement) {\n\t\t * connectedCallback() {\n\t\t * // Create any number of effects on connect.\n\t\t * this.createEffect(() => {...})\n\t\t * this.createEffect(() => {...})\n\t\t * this.createEffect(() => {...})\n\t\t * }\n\t\t *\n\t\t * disconnectedCallback() {\n\t\t * // Clean up all effects on disconnect.\n\t\t * this.clearEffects()\n\t\t * }\n\t\t * }\n\t\t * ```\n\t\t */\n\t\tclearEffects() {\n\t\t\tthis.stopEffects()\n\t\t\tthis.#effectFunctions = []\n\t\t}\n\n\t\t#owner: Owner | null = null\n\t\t#dispose: (() => void) | null = null\n\n\t\t#createEffect(fn: () => void) {\n\t\t\tconst owner = getOwner()\n\n\t\t\t// If nested in an existing owner (f.e. nested effect), delegate to\n\t\t\t// regular createEffect.\n\t\t\tif (owner) return createEffect(fn)\n\n\t\t\t// Store top-level effect functions so they can be replayed when\n\t\t\t// startEffects() is called\n\t\t\tif (!this.#isRestarting) this.#effectFunctions.push(fn)\n\n\t\t\t// If top-level call either attach to an existing root, or make a\n\t\t\t// new one if we don't have one yet.\n\t\t\tif (this.#owner) runWithOwner(this.#owner, () => createEffect(fn))\n\t\t\telse {\n\t\t\t\tcreateRoot(dispose => {\n\t\t\t\t\tthis.#owner = getOwner()\n\t\t\t\t\tthis.#dispose = () => {\n\t\t\t\t\t\tdispose()\n\t\t\t\t\t\tthis.#owner = null\n\t\t\t\t\t}\n\t\t\t\t\tcreateEffect(fn)\n\t\t\t\t})\n\t\t\t}\n\t\t}\n\t}\n\n\t;(EffectfulClass.prototype as any)[isInstance] = true\n\n\tObject.defineProperty(EffectfulClass, Symbol.hasInstance, {value: instanceCheck})\n\n\treturn EffectfulClass\n}\n\nObject.defineProperty(Effectful, Symbol.hasInstance, {value: instanceCheck})\n\nfunction instanceCheck(obj: any): boolean {\n\tif (!obj || typeof obj !== 'object') return false\n\treturn isInstance in obj\n}\n\n/**\n * Shortcut for instantiating or extending directly instead of using the mixin.\n * F.e.\n *\n * ```js\n * class Car extends Effects {\n * start() {\n * this.createEffect(() => {...})\n * this.createEffect(() => {...})\n * }\n * stop() {\n * this.stopEffects()\n * }\n * }\n *\n * const specialEffects = new Effects()\n * specialEffects.createEffect(() => {})\n * // ...later\n * specialEffects.stopEffects()\n * ```\n */\nexport class Effects extends Effectful(class {}) {}\n"],"mappings":"AAAA,SAAoBA,YAAY,EAAEC,UAAU,EAAEC,QAAQ,EAAEC,YAAY,QAAO,UAAU;AAGrF,MAAMC,UAAU,GAAGC,MAAM,CAAC,CAAC;;AAE3B;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,SAASA,CAA2BC,IAAO,EAAE;EAC5D,IAAIA,IAAI,CAACC,SAAS,YAAYF,SAAS,EACtC,MAAM,IAAIG,KAAK,CAAC,oEAAoE,CAAC;EAEtF,MAAMC,cAAc,SAASH,IAAI,CAAC;IACjC,CAACI,eAAe,GAAsB,EAAE;IACxC,CAACC,OAAO,GAAG,IAAI;;IAEf;AACF;AACA;AACA;AACA;IACEZ,YAAYA,CAACa,EAAc,EAAE;MAC5B,IAAI,CAACC,YAAY,CAAC,CAAC;MACnB,IAAI,CAAC,CAACd,YAAY,CAACa,EAAE,CAAC;IACvB;IAEA,CAACE,YAAY,GAAG,KAAK;;IAErB;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACED,YAAYA,CAAA,EAAG;MACd,IAAI,IAAI,CAAC,CAACF,OAAO,EAAE;MACnB,IAAI,CAAC,CAACA,OAAO,GAAG,IAAI;;MAEpB;MACA,IAAI,CAAC,CAACG,YAAY,GAAG,IAAI;MACzB,IAAI;QACH,KAAK,MAAMF,EAAE,IAAI,IAAI,CAAC,CAACF,eAAe,EAAE,IAAI,CAAC,CAACX,YAAY,CAACa,EAAE,CAAC;MAC/D,CAAC,SAAS;QACT,IAAI,CAAC,CAACE,YAAY,GAAG,KAAK;MAC3B;IACD;;IAEA;AACF;AACA;IACEC,WAAWA,CAAA,EAAG;MACb,IAAI,CAAC,IAAI,CAAC,CAACJ,OAAO,EAAE;MACpB,IAAI,CAAC,CAACA,OAAO,GAAG,KAAK;MAErB,IAAI,CAAC,CAACK,OAAO,GAAG,CAAC;IAClB;;IAEA;AACF;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;IACEC,YAAYA,CAAA,EAAG;MACd,IAAI,CAACF,WAAW,CAAC,CAAC;MAClB,IAAI,CAAC,CAACL,eAAe,GAAG,EAAE;IAC3B;IAEA,CAACQ,KAAK,GAAiB,IAAI;IAC3B,CAACF,OAAO,GAAwB,IAAI;IAEpC,CAACjB,YAAYoB,CAACP,EAAc,EAAE;MAC7B,MAAMM,KAAK,GAAGjB,QAAQ,CAAC,CAAC;;MAExB;MACA;MACA,IAAIiB,KAAK,EAAE,OAAOnB,YAAY,CAACa,EAAE,CAAC;;MAElC;MACA;MACA,IAAI,CAAC,IAAI,CAAC,CAACE,YAAY,EAAE,IAAI,CAAC,CAACJ,eAAe,CAACU,IAAI,CAACR,EAAE,CAAC;;MAEvD;MACA;MACA,IAAI,IAAI,CAAC,CAACM,KAAK,EAAEhB,YAAY,CAAC,IAAI,CAAC,CAACgB,KAAK,EAAE,MAAMnB,YAAY,CAACa,EAAE,CAAC,CAAC,MAC7D;QACJZ,UAAU,CAACgB,OAAO,IAAI;UACrB,IAAI,CAAC,CAACE,KAAK,GAAGjB,QAAQ,CAAC,CAAC;UACxB,IAAI,CAAC,CAACe,OAAO,GAAG,MAAM;YACrBA,OAAO,CAAC,CAAC;YACT,IAAI,CAAC,CAACE,KAAK,GAAG,IAAI;UACnB,CAAC;UACDnB,YAAY,CAACa,EAAE,CAAC;QACjB,CAAC,CAAC;MACH;IACD;EACD;EAEA;EAAEH,cAAc,CAACF,SAAS,CAASJ,UAAU,CAAC,GAAG,IAAI;EAErDkB,MAAM,CAACC,cAAc,CAACb,cAAc,EAAEL,MAAM,CAACmB,WAAW,EAAE;IAACC,KAAK,EAAEC;EAAa,CAAC,CAAC;EAEjF,OAAOhB,cAAc;AACtB;AAEAY,MAAM,CAACC,cAAc,CAACjB,SAAS,EAAED,MAAM,CAACmB,WAAW,EAAE;EAACC,KAAK,EAAEC;AAAa,CAAC,CAAC;AAE5E,SAASA,aAAaA,CAACC,GAAQ,EAAW;EACzC,IAAI,CAACA,GAAG,IAAI,OAAOA,GAAG,KAAK,QAAQ,EAAE,OAAO,KAAK;EACjD,OAAOvB,UAAU,IAAIuB,GAAG;AACzB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,MAAMC,OAAO,SAAStB,SAAS,CAAC,MAAM,EAAE,CAAC,CAAC","ignoreList":[]} \ No newline at end of file diff --git a/dist/mixins/Effectful.test.d.ts b/dist/mixins/Effectful.test.d.ts new file mode 100644 index 0000000..6dadd84 --- /dev/null +++ b/dist/mixins/Effectful.test.d.ts @@ -0,0 +1,2 @@ +export {}; +//# sourceMappingURL=Effectful.test.d.ts.map \ No newline at end of file diff --git a/dist/mixins/Effectful.test.d.ts.map b/dist/mixins/Effectful.test.d.ts.map new file mode 100644 index 0000000..e58cca6 --- /dev/null +++ b/dist/mixins/Effectful.test.d.ts.map @@ -0,0 +1 @@ +{"version":3,"file":"Effectful.test.d.ts","sourceRoot":"","sources":["../../src/mixins/Effectful.test.ts"],"names":[],"mappings":""} \ No newline at end of file diff --git a/dist/mixins/Effectful.test.js b/dist/mixins/Effectful.test.js new file mode 100644 index 0000000..a3be9b5 --- /dev/null +++ b/dist/mixins/Effectful.test.js @@ -0,0 +1,249 @@ +function _applyDecs(e, t, n, r, o, i) { var a, c, u, s, f, l, p, d = Symbol.metadata || Symbol.for("Symbol.metadata"), m = Object.defineProperty, h = Object.create, y = [h(null), h(null)], v = t.length; function g(t, n, r) { return function (o, i) { n && (i = o, o = e); for (var a = 0; a < t.length; a++) i = t[a].apply(o, r ? [i] : []); return r ? i : o; }; } function b(e, t, n, r) { if ("function" != typeof e && (r || void 0 !== e)) throw new TypeError(t + " must " + (n || "be") + " a function" + (r ? "" : " or undefined")); return e; } function applyDec(e, t, n, r, o, i, u, s, f, l, p) { function d(e) { if (!p(e)) throw new TypeError("Attempted to access private element on non-instance"); } var h = [].concat(t[0]), v = t[3], w = !u, D = 1 === o, S = 3 === o, j = 4 === o, E = 2 === o; function I(t, n, r) { return function (o, i) { return n && (i = o, o = e), r && r(o), P[t].call(o, i); }; } if (!w) { var P = {}, k = [], F = S ? "get" : j || D ? "set" : "value"; if (f ? (l || D ? P = { get: _setFunctionName(function () { return v(this); }, r, "get"), set: function (e) { t[4](this, e); } } : P[F] = v, l || _setFunctionName(P[F], r, E ? "" : F)) : l || (P = Object.getOwnPropertyDescriptor(e, r)), !l && !f) { if ((c = y[+s][r]) && 7 !== (c ^ o)) throw Error("Decorating two elements with the same name (" + P[F].name + ") is not supported yet"); y[+s][r] = o < 3 ? 1 : o; } } for (var N = e, O = h.length - 1; O >= 0; O -= n ? 2 : 1) { var T = b(h[O], "A decorator", "be", !0), z = n ? h[O - 1] : void 0, A = {}, H = { kind: ["field", "accessor", "method", "getter", "setter", "class"][o], name: r, metadata: a, addInitializer: function (e, t) { if (e.v) throw new TypeError("attempted to call addInitializer after decoration was finished"); b(t, "An initializer", "be", !0), i.push(t); }.bind(null, A) }; if (w) c = T.call(z, N, H), A.v = 1, b(c, "class decorators", "return") && (N = c);else if (H.static = s, H.private = f, c = H.access = { has: f ? p.bind() : function (e) { return r in e; } }, j || (c.get = f ? E ? function (e) { return d(e), P.value; } : I("get", 0, d) : function (e) { return e[r]; }), E || S || (c.set = f ? I("set", 0, d) : function (e, t) { e[r] = t; }), N = T.call(z, D ? { get: P.get, set: P.set } : P[F], H), A.v = 1, D) { if ("object" == typeof N && N) (c = b(N.get, "accessor.get")) && (P.get = c), (c = b(N.set, "accessor.set")) && (P.set = c), (c = b(N.init, "accessor.init")) && k.unshift(c);else if (void 0 !== N) throw new TypeError("accessor decorators must return an object with get, set, or init properties or undefined"); } else b(N, (l ? "field" : "method") + " decorators", "return") && (l ? k.unshift(N) : P[F] = N); } return o < 2 && u.push(g(k, s, 1), g(i, s, 0)), l || w || (f ? D ? u.splice(-1, 0, I("get", s), I("set", s)) : u.push(E ? P[F] : b.call.bind(P[F])) : m(e, r, P)), N; } function w(e) { return m(e, d, { configurable: !0, enumerable: !0, value: a }); } return void 0 !== i && (a = i[d]), a = h(null == a ? null : a), f = [], l = function (e) { e && f.push(g(e)); }, p = function (t, r) { for (var i = 0; i < n.length; i++) { var a = n[i], c = a[1], l = 7 & c; if ((8 & c) == t && !l == r) { var p = a[2], d = !!a[3], m = 16 & c; applyDec(t ? e : e.prototype, a, m, d ? "#" + p : _toPropertyKey(p), l, l < 2 ? [] : t ? s = s || [] : u = u || [], f, !!t, d, r, t && d ? function (t) { return _checkInRHS(t) === e; } : o); } } }, p(8, 0), p(0, 0), p(8, 1), p(0, 1), l(u), l(s), c = f, v || w(e), { e: c, get c() { var n = []; return v && [w(e = applyDec(e, [t], r, e.name, 5, n)), g(n, 1)]; } }; } +function _toPropertyKey(t) { var i = _toPrimitive(t, "string"); return "symbol" == typeof i ? i : i + ""; } +function _toPrimitive(t, r) { if ("object" != typeof t || !t) return t; var e = t[Symbol.toPrimitive]; if (void 0 !== e) { var i = e.call(t, r || "default"); if ("object" != typeof i) return i; throw new TypeError("@@toPrimitive must return a primitive value."); } return ("string" === r ? String : Number)(t); } +function _setFunctionName(e, t, n) { "symbol" == typeof t && (t = (t = t.description) ? "[" + t + "]" : ""); try { Object.defineProperty(e, "name", { configurable: !0, value: n ? n + " " + t : t }); } catch (e) {} return e; } +function _checkInRHS(e) { if (Object(e) !== e) throw TypeError("right-hand side of 'in' should be an object, got " + (null !== e ? typeof e : "null")); return e; } +import { createSignal } from 'solid-js'; +import { Effectful, Effects } from './Effectful.js'; +import { signal } from '../decorators/signal.js'; +import { testElementEffects } from '../index.test.js'; +describe('classy-solid', () => { + describe('Effectful mixin / Effects', () => { + it('createEffect runs immediately, stopEffects stops further runs, startEffects runs effects again', () => { + const [s, setS] = createSignal(1); + const e = new Effects(); + let last = null; + let runs = 0; + e.createEffect(() => { + runs++; + last = s(); + }); + expect(last).toBe(1); + expect(runs).toBe(1); + setS(2); + expect(last).toBe(2); + expect(runs).toBe(2); + + // later, stop effects when done (f.e. when custom element disconnected from DOM)... + + e.stopEffects(); + setS(3); + expect(last).toBe(2); + expect(runs).toBe(2); + + // later, start effects again (f.e. when custom element reconnected to DOM)... + + e.startEffects(); + expect(last).toBe(3); + expect(runs).toBe(3); + setS(4); + expect(last).toBe(4); + expect(runs).toBe(4); + + // Clear all effects. + e.clearEffects(); + setS(5); + expect(last).toBe(4); + expect(runs).toBe(4); + e.startEffects(); // no effects to start + expect(last).toBe(4); + expect(runs).toBe(4); + setS(6); + expect(last).toBe(4); + expect(runs).toBe(4); + + // Add a new effect after clearing previous ones + + e.createEffect(() => { + runs++; + last = s(); + }); + expect(last).toBe(6); + expect(runs).toBe(5); + setS(7); + expect(last).toBe(7); + expect(runs).toBe(6); + }); + it('startEffects does not duplicate effects', () => { + const [s, setS] = createSignal(1); + const e = new Effects(); + let runs = 0; + e.createEffect(() => { + runs++; + s(); + }); + expect(runs).toBe(1); + e.startEffects(); // should not duplicate effects + + setS(2); + expect(runs).toBe(2); + }); + it('clearEffects prevents effects from restarting', () => { + const [s, setS] = createSignal(1); + const e = new Effects(); + let runs = 0; + e.createEffect(() => { + runs++; + s(); + }); + expect(runs).toBe(1); + e.clearEffects(); + setS(2); + expect(runs).toBe(1); + e.startEffects(); // should not restart any effects + + setS(3); + expect(runs).toBe(1); + }); + it('can be extended from', () => { + let _init_a, _init_extra_a; + class MyEffects extends Effects { + static { + [_init_a, _init_extra_a] = _applyDecs(this, [], [[signal, 0, "a"]], 0, void 0, Effects).e; + } + double = 0; + constructor() { + super(), _init_extra_a(this); + this.createEffect(() => { + this.double = this.a * 2; + }); + } + a = _init_a(this, 1); + } + const me = new MyEffects(); + expect(me.double).toBe(2); + me.a = 5; + expect(me.double).toBe(10); + me.stopEffects(); + me.a = 10; + expect(me.double).toBe(10); + me.startEffects(); + expect(me.double).toBe(20); + }); + it('works with multiple Effectful-derived classes', () => { + var _Effectful; + let _init_baseSignal, _init_extra_baseSignal, _init_derivedSignal, _init_extra_derivedSignal; + class Base extends (_Effectful = Effectful(Object)) { + static { + [_init_baseSignal, _init_extra_baseSignal] = _applyDecs(this, [], [[signal, 0, "baseSignal"]], 0, void 0, _Effectful).e; + } + baseSignal = _init_baseSignal(this, 1); + baseValue = (_init_extra_baseSignal(this), 0); + constructor() { + super(); + this.createEffect(() => { + this.baseValue = this.baseSignal * 10; + }); + } + } + class Derived extends Base { + static { + [_init_derivedSignal, _init_extra_derivedSignal] = _applyDecs(this, [], [[signal, 0, "derivedSignal"]], 0, void 0, Base).e; + } + derivedSignal = _init_derivedSignal(this, 2); + derivedValue = (_init_extra_derivedSignal(this), 0); + constructor() { + super(); + this.createEffect(() => { + this.derivedValue = this.derivedSignal * 100; + }); + } + } + const d = new Derived(); + expect(d.baseValue).toBe(10); + expect(d.derivedValue).toBe(200); + d.baseSignal = 3; + expect(d.baseValue).toBe(30); + d.derivedSignal = 4; + expect(d.derivedValue).toBe(400); + d.stopEffects(); + d.baseSignal = 5; + d.derivedSignal = 6; + expect(d.baseValue).toBe(30); + expect(d.derivedValue).toBe(400); + d.startEffects(); + expect(d.baseValue).toBe(50); + expect(d.derivedValue).toBe(600); + }); + it('supports instanceof checks', () => { + class MyEffectful extends Effectful(Object) {} + const me = new MyEffectful(); + expect(me instanceof Effectful).toBe(true); + expect(me instanceof MyEffectful).toBe(true); + const e = new Effects(); + expect(e instanceof Effects).toBe(true); + expect(e instanceof Effectful).toBe(true); + }); + it('allows nested createEffect calls', () => { + const [a, setA] = createSignal(0); + const [b, setB] = createSignal(0); + const e = new Effects(); + let outerRuns = 0; + let innerRuns = 0; + e.createEffect(function outer() { + outerRuns++; + a(); + e.createEffect(function inner() { + innerRuns++; + b(); + }); + }); + expect(outerRuns).toBe(1); + expect(innerRuns).toBe(1); + e.startEffects(); // should not duplicate effects (already started) + + expect(outerRuns).toBe(1); + expect(innerRuns).toBe(1); + setA(1); + expect(outerRuns).toBe(2); + expect(innerRuns).toBe(2); // inner effect runs because outer effect re-ran + + setB(1); + expect(outerRuns).toBe(2); + expect(innerRuns).toBe(3); // inner effect runs independently + + e.stopEffects(); + expect(outerRuns).toBe(2); + expect(innerRuns).toBe(3); + e.startEffects(); + expect(outerRuns).toBe(3); + expect(innerRuns).toBe(4); // inner effect runs because outer effect re-ran + + setB(2); + expect(outerRuns).toBe(3); + expect(innerRuns).toBe(5); // inner effect runs independently + }); + describe('invalid usages', () => { + it('prevents multiple Effectful mixin applications', () => { + expect(() => { + class Base extends Effectful(Object) {} + class Derived extends Effectful(Base) {} + Derived; + }).toThrow('Class already extends Effectful, no need to apply the mixin again.'); + }); + }); + describe('usage with custom elements', () => { + it('createEffect in connectedCallback, clearEffects in disconnectedCallback', () => { + const el = document.createElement('my-element'); + expect(el.result).toBe(0); + expect(el.runs).toBe(0); + testElementEffects(el); + }); + it('createEffect in constructor, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => { + const el = document.createElement('my-element2'); + expect(el.result).toBe(1 + 2); + expect(el.runs).toBe(1); // already ran in constructor + + testElementEffects(el); + }); + it('@effect methods, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => { + const el = document.createElement('my-element3'); + expect(el.result).toBe(1 + 2); + expect(el.runs).toBe(1); // already ran in constructor + + testElementEffects(el); + }); + }); + }); +}); +//# sourceMappingURL=Effectful.test.js.map \ No newline at end of file diff --git a/dist/mixins/Effectful.test.js.map b/dist/mixins/Effectful.test.js.map new file mode 100644 index 0000000..fad5254 --- /dev/null +++ b/dist/mixins/Effectful.test.js.map @@ -0,0 +1 @@ +{"version":3,"file":"Effectful.test.js","names":["createSignal","Effectful","Effects","signal","testElementEffects","describe","it","s","setS","e","last","runs","createEffect","expect","toBe","stopEffects","startEffects","clearEffects","_init_a","_init_extra_a","MyEffects","_applyDecs","double","constructor","a","me","_Effectful","_init_baseSignal","_init_extra_baseSignal","_init_derivedSignal","_init_extra_derivedSignal","Base","Object","baseSignal","baseValue","Derived","derivedSignal","derivedValue","d","MyEffectful","setA","b","setB","outerRuns","innerRuns","outer","inner","toThrow","el","document","createElement","result"],"sources":["../../src/mixins/Effectful.test.ts"],"sourcesContent":["import {createSignal} from 'solid-js'\nimport {Effectful, Effects} from './Effectful.js'\nimport {signal} from '../decorators/signal.js'\nimport {MyElement, MyElement2, MyElement3, testElementEffects} from '../index.test.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('Effectful mixin / Effects', () => {\n\t\tit('createEffect runs immediately, stopEffects stops further runs, startEffects runs effects again', () => {\n\t\t\tconst [s, setS] = createSignal(1)\n\t\t\tconst e = new Effects()\n\n\t\t\tlet last = null\n\t\t\tlet runs = 0\n\t\t\te.createEffect(() => {\n\t\t\t\truns++\n\t\t\t\tlast = s()\n\t\t\t})\n\n\t\t\texpect(last).toBe(1)\n\t\t\texpect(runs).toBe(1)\n\n\t\t\tsetS(2)\n\t\t\texpect(last).toBe(2)\n\t\t\texpect(runs).toBe(2)\n\n\t\t\t// later, stop effects when done (f.e. when custom element disconnected from DOM)...\n\n\t\t\te.stopEffects()\n\t\t\tsetS(3)\n\t\t\texpect(last).toBe(2)\n\t\t\texpect(runs).toBe(2)\n\n\t\t\t// later, start effects again (f.e. when custom element reconnected to DOM)...\n\n\t\t\te.startEffects()\n\t\t\texpect(last).toBe(3)\n\t\t\texpect(runs).toBe(3)\n\n\t\t\tsetS(4)\n\t\t\texpect(last).toBe(4)\n\t\t\texpect(runs).toBe(4)\n\n\t\t\t// Clear all effects.\n\t\t\te.clearEffects()\n\t\t\tsetS(5)\n\t\t\texpect(last).toBe(4)\n\t\t\texpect(runs).toBe(4)\n\n\t\t\te.startEffects() // no effects to start\n\t\t\texpect(last).toBe(4)\n\t\t\texpect(runs).toBe(4)\n\t\t\tsetS(6)\n\t\t\texpect(last).toBe(4)\n\t\t\texpect(runs).toBe(4)\n\n\t\t\t// Add a new effect after clearing previous ones\n\n\t\t\te.createEffect(() => {\n\t\t\t\truns++\n\t\t\t\tlast = s()\n\t\t\t})\n\t\t\texpect(last).toBe(6)\n\t\t\texpect(runs).toBe(5)\n\n\t\t\tsetS(7)\n\t\t\texpect(last).toBe(7)\n\t\t\texpect(runs).toBe(6)\n\t\t})\n\n\t\tit('startEffects does not duplicate effects', () => {\n\t\t\tconst [s, setS] = createSignal(1)\n\t\t\tconst e = new Effects()\n\n\t\t\tlet runs = 0\n\t\t\te.createEffect(() => {\n\t\t\t\truns++\n\t\t\t\ts()\n\t\t\t})\n\n\t\t\texpect(runs).toBe(1)\n\n\t\t\te.startEffects() // should not duplicate effects\n\n\t\t\tsetS(2)\n\t\t\texpect(runs).toBe(2)\n\t\t})\n\n\t\tit('clearEffects prevents effects from restarting', () => {\n\t\t\tconst [s, setS] = createSignal(1)\n\t\t\tconst e = new Effects()\n\n\t\t\tlet runs = 0\n\t\t\te.createEffect(() => {\n\t\t\t\truns++\n\t\t\t\ts()\n\t\t\t})\n\n\t\t\texpect(runs).toBe(1)\n\n\t\t\te.clearEffects()\n\n\t\t\tsetS(2)\n\t\t\texpect(runs).toBe(1)\n\n\t\t\te.startEffects() // should not restart any effects\n\n\t\t\tsetS(3)\n\t\t\texpect(runs).toBe(1)\n\t\t})\n\n\t\tit('can be extended from', () => {\n\t\t\tclass MyEffects extends Effects {\n\t\t\t\tdouble = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsuper()\n\t\t\t\t\tthis.createEffect(() => {\n\t\t\t\t\t\tthis.double = this.a * 2\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\t@signal a = 1\n\t\t\t}\n\n\t\t\tconst me = new MyEffects()\n\t\t\texpect(me.double).toBe(2)\n\n\t\t\tme.a = 5\n\t\t\texpect(me.double).toBe(10)\n\n\t\t\tme.stopEffects()\n\t\t\tme.a = 10\n\t\t\texpect(me.double).toBe(10)\n\n\t\t\tme.startEffects()\n\t\t\texpect(me.double).toBe(20)\n\t\t})\n\n\t\tit('works with multiple Effectful-derived classes', () => {\n\t\t\tclass Base extends Effectful(Object) {\n\t\t\t\t@signal baseSignal = 1\n\t\t\t\tbaseValue = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsuper()\n\t\t\t\t\tthis.createEffect(() => {\n\t\t\t\t\t\tthis.baseValue = this.baseSignal * 10\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclass Derived extends Base {\n\t\t\t\t@signal derivedSignal = 2\n\t\t\t\tderivedValue = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsuper()\n\t\t\t\t\tthis.createEffect(() => {\n\t\t\t\t\t\tthis.derivedValue = this.derivedSignal * 100\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst d = new Derived()\n\t\t\texpect(d.baseValue).toBe(10)\n\t\t\texpect(d.derivedValue).toBe(200)\n\n\t\t\td.baseSignal = 3\n\t\t\texpect(d.baseValue).toBe(30)\n\n\t\t\td.derivedSignal = 4\n\t\t\texpect(d.derivedValue).toBe(400)\n\n\t\t\td.stopEffects()\n\t\t\td.baseSignal = 5\n\t\t\td.derivedSignal = 6\n\t\t\texpect(d.baseValue).toBe(30)\n\t\t\texpect(d.derivedValue).toBe(400)\n\n\t\t\td.startEffects()\n\t\t\texpect(d.baseValue).toBe(50)\n\t\t\texpect(d.derivedValue).toBe(600)\n\t\t})\n\n\t\tit('supports instanceof checks', () => {\n\t\t\tclass MyEffectful extends Effectful(Object) {}\n\n\t\t\tconst me = new MyEffectful()\n\t\t\texpect(me instanceof Effectful).toBe(true)\n\t\t\texpect(me instanceof MyEffectful).toBe(true)\n\n\t\t\tconst e = new Effects()\n\t\t\texpect(e instanceof Effects).toBe(true)\n\t\t\texpect(e instanceof Effectful).toBe(true)\n\t\t})\n\n\t\tit('allows nested createEffect calls', () => {\n\t\t\tconst [a, setA] = createSignal(0)\n\t\t\tconst [b, setB] = createSignal(0)\n\t\t\tconst e = new Effects()\n\n\t\t\tlet outerRuns = 0\n\t\t\tlet innerRuns = 0\n\n\t\t\te.createEffect(function outer() {\n\t\t\t\touterRuns++\n\t\t\t\ta()\n\n\t\t\t\te.createEffect(function inner() {\n\t\t\t\t\tinnerRuns++\n\t\t\t\t\tb()\n\t\t\t\t})\n\t\t\t})\n\n\t\t\texpect(outerRuns).toBe(1)\n\t\t\texpect(innerRuns).toBe(1)\n\n\t\t\te.startEffects() // should not duplicate effects (already started)\n\n\t\t\texpect(outerRuns).toBe(1)\n\t\t\texpect(innerRuns).toBe(1)\n\n\t\t\tsetA(1)\n\t\t\texpect(outerRuns).toBe(2)\n\t\t\texpect(innerRuns).toBe(2) // inner effect runs because outer effect re-ran\n\n\t\t\tsetB(1)\n\t\t\texpect(outerRuns).toBe(2)\n\t\t\texpect(innerRuns).toBe(3) // inner effect runs independently\n\n\t\t\te.stopEffects()\n\n\t\t\texpect(outerRuns).toBe(2)\n\t\t\texpect(innerRuns).toBe(3)\n\n\t\t\te.startEffects()\n\n\t\t\texpect(outerRuns).toBe(3)\n\t\t\texpect(innerRuns).toBe(4) // inner effect runs because outer effect re-ran\n\n\t\t\tsetB(2)\n\t\t\texpect(outerRuns).toBe(3)\n\t\t\texpect(innerRuns).toBe(5) // inner effect runs independently\n\t\t})\n\n\t\tdescribe('invalid usages', () => {\n\t\t\tit('prevents multiple Effectful mixin applications', () => {\n\t\t\t\texpect(() => {\n\t\t\t\t\tclass Base extends Effectful(Object) {}\n\t\t\t\t\tclass Derived extends Effectful(Base) {}\n\t\t\t\t\tDerived\n\t\t\t\t}).toThrow('Class already extends Effectful, no need to apply the mixin again.')\n\t\t\t})\n\t\t})\n\n\t\tdescribe('usage with custom elements', () => {\n\t\t\tit('createEffect in connectedCallback, clearEffects in disconnectedCallback', () => {\n\t\t\t\tconst el = document.createElement('my-element') as MyElement\n\t\t\t\texpect(el.result).toBe(0)\n\t\t\t\texpect(el.runs).toBe(0)\n\n\t\t\t\ttestElementEffects(el)\n\t\t\t})\n\n\t\t\tit('createEffect in constructor, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => {\n\t\t\t\tconst el = document.createElement('my-element2') as MyElement2\n\t\t\t\texpect(el.result).toBe(1 + 2)\n\t\t\t\texpect(el.runs).toBe(1) // already ran in constructor\n\n\t\t\t\ttestElementEffects(el)\n\t\t\t})\n\n\t\t\tit('@effect methods, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => {\n\t\t\t\tconst el = document.createElement('my-element3') as MyElement3\n\t\t\t\texpect(el.result).toBe(1 + 2)\n\t\t\t\texpect(el.runs).toBe(1) // already ran in constructor\n\n\t\t\t\ttestElementEffects(el)\n\t\t\t})\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/signals/memoify.d.ts b/dist/signals/memoify.d.ts index 19a12aa..b93a9df 100644 --- a/dist/signals/memoify.d.ts +++ b/dist/signals/memoify.d.ts @@ -1,3 +1,6 @@ +import type { MemberStat } from '../decorators/types.js'; +/** @private internal only */ +export declare function setMemoifyMemberStat(stat: MemberStat): void; /** * Convert properties on an object into Solid.js memoized properties. * @@ -68,6 +71,4 @@ */ export declare function memoify(obj: T): T; export declare function memoify(obj: T, ...props: (keyof T)[]): T; -/** This overload is for use by the @memo decorator */ -export declare function memoify(obj: T, isAutoAccessor: boolean, ...props: (keyof T)[]): T; //# sourceMappingURL=memoify.d.ts.map \ No newline at end of file diff --git a/dist/signals/memoify.d.ts.map b/dist/signals/memoify.d.ts.map index bd2260c..6a182a1 100644 --- a/dist/signals/memoify.d.ts.map +++ b/dist/signals/memoify.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"memoify.d.ts","sourceRoot":"","sources":["../../src/signals/memoify.ts"],"names":[],"mappings":"AASA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AACvE,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAC3E,sDAAsD;AACtD,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,cAAc,EAAE,OAAO,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA"} \ No newline at end of file +{"version":3,"file":"memoify.d.ts","sourceRoot":"","sources":["../../src/signals/memoify.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAY,UAAU,EAAC,MAAM,wBAAwB,CAAA;AAMjE,6BAA6B;AAC7B,wBAAgB,oBAAoB,CAAC,IAAI,EAAE,UAAU,QAEpD;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmEG;AACH,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AACvE,wBAAgB,OAAO,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA"} \ No newline at end of file diff --git a/dist/signals/memoify.js b/dist/signals/memoify.js index 232b01d..c01ef53 100644 --- a/dist/signals/memoify.js +++ b/dist/signals/memoify.js @@ -3,6 +3,12 @@ import { createWritableMemo } from '@solid-primitives/memo'; import { getInheritedDescriptor } from 'lowclass/dist/getInheritedDescriptor.js'; import { isMemoGetter, isSignalGetter } from '../_state.js'; const Undefined = Symbol(); +let memberStat = null; + +/** @private internal only */ +export function setMemoifyMemberStat(stat) { + memberStat = stat; +} /** * Convert properties on an object into Solid.js memoized properties. @@ -73,25 +79,41 @@ const Undefined = Symbol(); * ``` */ -/** This overload is for use by the @memo decorator */ - -export function memoify(obj, propOrBoolean, ...props) { - const isAutoAccessor = typeof propOrBoolean === 'boolean' ? propOrBoolean : false; - props = typeof propOrBoolean === 'boolean' ? props : typeof propOrBoolean !== 'undefined' ? [propOrBoolean, ...props] : props; - +export function memoify(obj, ...props) { // If no props specified, use all keys (including symbols) const keys = props.length ? props : [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]; + const isAutoAccessor = memberStat?.type === 'memo-auto-accessor'; for (const key of keys) { const descriptor = getInheritedDescriptor(obj, key); - if (!descriptor) continue; + if (!descriptor) { + memberStat = null; + continue; + } // Skip if already memoified or signalified - if (descriptor.get && (isMemoGetter.has(descriptor.get) || isSignalGetter.has(descriptor.get))) continue; + if (descriptor.get && (isMemoGetter.has(descriptor.get) || isSignalGetter.has(descriptor.get))) { + memberStat = null; + continue; + } // Handle methods (function-valued properties) if (typeof descriptor.value === 'function' || isAutoAccessor) { + // Skip base class memoify if a subclass is overriding an auto-accessor memo. + const leafmostMemberValue = isAutoAccessor ? descriptor.get : descriptor.value; + if (memberStat && leafmostMemberValue !== memberStat.value) { + memberStat = null; + continue; + } const fn = isAutoAccessor ? descriptor.get?.call(obj) : descriptor.value; - if (typeof fn !== 'function') continue; + if (typeof fn !== 'function') { + // Throw in decorator mode only. + if (memberStat) { + memberStat = null; + throw new Error(`memo value for "${String(key)}" is not a function: ${fn}`); + } + // Otherwise just skip non-function properties (f.e. using memoify(obj) directly on a plain object, without decorators). + else continue; + } const name = fn.name || String(key); let value; @@ -124,7 +146,11 @@ export function memoify(obj, propOrBoolean, ...props) { } // Handle accessors - else if (descriptor.get) { + else if (descriptor.get || descriptor.set) { + if (!descriptor.get) { + memberStat = null; + throw new Error(`Cannot memoify accessor "${String(key)}" without a getter.`); + } let get; let set; @@ -147,11 +173,15 @@ export function memoify(obj, propOrBoolean, ...props) { enumerable: descriptor.enumerable }); isMemoGetter.add(get); + } else { + // Throw in decorator mode only. + if (memberStat) { + memberStat = null; + throw new Error(`memo value for "${String(key)}" is not a function: ${descriptor.value}`); + } } - - // Skip non-function, non-accessor properties - continue; } + memberStat = null; return obj; } //# sourceMappingURL=memoify.js.map \ No newline at end of file diff --git a/dist/signals/memoify.js.map b/dist/signals/memoify.js.map index 8340818..ab76793 100644 --- a/dist/signals/memoify.js.map +++ b/dist/signals/memoify.js.map @@ -1 +1 @@ -{"version":3,"file":"memoify.js","names":["createMemo","createWritableMemo","getInheritedDescriptor","isMemoGetter","isSignalGetter","Undefined","Symbol","memoify","obj","propOrBoolean","props","isAutoAccessor","keys","length","Object","getOwnPropertySymbols","key","descriptor","get","has","value","fn","call","name","String","val","Error","set","defineProperty","configurable","enumerable","add"],"sources":["../../src/signals/memoify.ts"],"sourcesContent":["import {createMemo} from 'solid-js'\nimport {createWritableMemo} from '@solid-primitives/memo'\nimport {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js'\nimport {isMemoGetter, isSignalGetter} from '../_state.js'\n\ntype AnyObject = Record\n\nconst Undefined = Symbol()\n\n/**\n * Convert properties on an object into Solid.js memoized properties.\n *\n * There are two ways to use this:\n *\n * 1. Define which properties to convert to memoized properties by providing\n * property names as trailing arguments. Properties that are not function-valued\n * or accessors will be ignored.\n * 2. If no property names are provided, all function-valued properties and\n * accessors on the object will be automatically converted to memoized\n * properties.\n *\n * If any property is already memoified with `memoify()`, or already signalified\n * with `signalify()`, it will be skipped.\n *\n * Example with a plain object:\n *\n * ```js\n * import {memoify, signalify} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * const obj = {\n * a: 1,\n * b: 2,\n * get sum() {\n * return this.a + this.b\n * }\n * }\n *\n * signalify(obj, 'a', 'b')\n * memoify(obj, 'sum')\n *\n * createEffect(() => {\n * console.log('sum:', obj.sum)\n * })\n *\n * obj.a = 3 // updates sum to 5\n * ```\n *\n * Example with a class:\n *\n * ```js\n * import {memoify, signalify} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * class Example {\n * a = 1\n * b = 2\n *\n * get sum() {\n * return this.a + this.b\n * }\n *\n * constructor() {\n * signalify(this, 'a', 'b')\n * memoify(this, 'sum')\n * }\n * }\n *\n * const ex = new Example()\n *\n * createEffect(() => {\n * console.log('sum:', ex.sum)\n * })\n *\n * ex.a = 3 // updates sum to 5\n * ```\n */\nexport function memoify(obj: T): T\nexport function memoify(obj: T, ...props: (keyof T)[]): T\n/** This overload is for use by the @memo decorator */\nexport function memoify(obj: T, isAutoAccessor: boolean, ...props: (keyof T)[]): T\nexport function memoify(obj: AnyObject, propOrBoolean?: PropertyKey | boolean, ...props: PropertyKey[]) {\n\tconst isAutoAccessor = typeof propOrBoolean === 'boolean' ? propOrBoolean : false\n\n\tprops =\n\t\ttypeof propOrBoolean === 'boolean'\n\t\t\t? props\n\t\t\t: typeof propOrBoolean !== 'undefined'\n\t\t\t? [propOrBoolean, ...props]\n\t\t\t: props\n\n\t// If no props specified, use all keys (including symbols)\n\tconst keys: PropertyKey[] = props.length ? props : [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]\n\n\tfor (const key of keys) {\n\t\tconst descriptor = getInheritedDescriptor(obj, key)\n\n\t\tif (!descriptor) continue\n\n\t\t// Skip if already memoified or signalified\n\t\tif (descriptor.get && (isMemoGetter.has(descriptor.get) || isSignalGetter.has(descriptor.get))) continue\n\n\t\t// Handle methods (function-valued properties)\n\t\tif (typeof descriptor.value === 'function' || isAutoAccessor) {\n\t\t\tconst fn = isAutoAccessor ? descriptor.get?.call(obj) : descriptor.value\n\t\t\tif (typeof fn !== 'function') continue\n\n\t\t\tconst name = fn.name || String(key)\n\t\t\tlet value\n\n\t\t\t// Readonly memo: arity 0\n\t\t\tif (fn.length === 0) {\n\t\t\t\tconst get = createMemo(() => fn.call(obj))\n\n\t\t\t\tvalue = (val = Undefined) => {\n\t\t\t\t\tif (val === Undefined) return get()\n\t\t\t\t\tthrow new Error(`Cannot set readonly memoized method \"${String(key)}\".`)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Writable memo: arity > 0\n\t\t\telse {\n\t\t\t\tconst [get, set] = createWritableMemo(() => fn.call(obj))\n\n\t\t\t\tvalue = (val: unknown = Undefined) => {\n\t\t\t\t\tif (val === Undefined) return get()\n\t\t\t\t\tset(typeof val === 'function' ? () => val : val)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tObject.defineProperty(value, 'name', {value: name, configurable: true})\n\t\t\tObject.defineProperty(obj, key, {value, configurable: true, enumerable: descriptor.enumerable})\n\t\t\tisMemoGetter.add(value)\n\t\t}\n\n\t\t// Handle accessors\n\t\telse if (descriptor.get) {\n\t\t\tlet get\n\t\t\tlet set: ((val: unknown) => void) | undefined\n\n\t\t\t// Readonly memo: getter only\n\t\t\tif (!descriptor.set) get = createMemo(() => descriptor.get!.call(obj))\n\t\t\t// Writable memo: getter and setter\n\t\t\telse [get, set] = createWritableMemo(() => descriptor.get!.call(obj))\n\n\t\t\tObject.defineProperty(get, 'name', {value: String(key), configurable: true})\n\t\t\tif (set) Object.defineProperty(set, 'name', {value: String(key), configurable: true})\n\t\t\tObject.defineProperty(obj, key, {\n\t\t\t\tget,\n\t\t\t\tset: set && (val => (typeof val === 'function' ? set(() => val) : set(val))),\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: descriptor.enumerable,\n\t\t\t})\n\t\t\tisMemoGetter.add(get)\n\t\t}\n\n\t\t// Skip non-function, non-accessor properties\n\t\tcontinue\n\t}\n\n\treturn obj\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"memoify.js","names":["createMemo","createWritableMemo","getInheritedDescriptor","isMemoGetter","isSignalGetter","Undefined","Symbol","memberStat","setMemoifyMemberStat","stat","memoify","obj","props","keys","length","Object","getOwnPropertySymbols","isAutoAccessor","type","key","descriptor","get","has","value","leafmostMemberValue","fn","call","Error","String","name","val","set","defineProperty","configurable","enumerable","add"],"sources":["../../src/signals/memoify.ts"],"sourcesContent":["import {createMemo} from 'solid-js'\nimport {createWritableMemo} from '@solid-primitives/memo'\nimport {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js'\nimport {isMemoGetter, isSignalGetter} from '../_state.js'\nimport type {AnyObject, MemberStat} from '../decorators/types.js'\n\nconst Undefined = Symbol()\n\nlet memberStat: MemberStat | null = null\n\n/** @private internal only */\nexport function setMemoifyMemberStat(stat: MemberStat) {\n\tmemberStat = stat\n}\n\n/**\n * Convert properties on an object into Solid.js memoized properties.\n *\n * There are two ways to use this:\n *\n * 1. Define which properties to convert to memoized properties by providing\n * property names as trailing arguments. Properties that are not function-valued\n * or accessors will be ignored.\n * 2. If no property names are provided, all function-valued properties and\n * accessors on the object will be automatically converted to memoized\n * properties.\n *\n * If any property is already memoified with `memoify()`, or already signalified\n * with `signalify()`, it will be skipped.\n *\n * Example with a plain object:\n *\n * ```js\n * import {memoify, signalify} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * const obj = {\n * a: 1,\n * b: 2,\n * get sum() {\n * return this.a + this.b\n * }\n * }\n *\n * signalify(obj, 'a', 'b')\n * memoify(obj, 'sum')\n *\n * createEffect(() => {\n * console.log('sum:', obj.sum)\n * })\n *\n * obj.a = 3 // updates sum to 5\n * ```\n *\n * Example with a class:\n *\n * ```js\n * import {memoify, signalify} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * class Example {\n * a = 1\n * b = 2\n *\n * get sum() {\n * return this.a + this.b\n * }\n *\n * constructor() {\n * signalify(this, 'a', 'b')\n * memoify(this, 'sum')\n * }\n * }\n *\n * const ex = new Example()\n *\n * createEffect(() => {\n * console.log('sum:', ex.sum)\n * })\n *\n * ex.a = 3 // updates sum to 5\n * ```\n */\nexport function memoify(obj: T): T\nexport function memoify(obj: T, ...props: (keyof T)[]): T\nexport function memoify(obj: AnyObject, ...props: PropertyKey[]) {\n\t// If no props specified, use all keys (including symbols)\n\tconst keys: PropertyKey[] = props.length ? props : [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)]\n\n\tconst isAutoAccessor = memberStat?.type === 'memo-auto-accessor'\n\n\tfor (const key of keys) {\n\t\tconst descriptor = getInheritedDescriptor(obj, key)\n\n\t\tif (!descriptor) {\n\t\t\tmemberStat = null\n\t\t\tcontinue\n\t\t}\n\n\t\t// Skip if already memoified or signalified\n\t\tif (descriptor.get && (isMemoGetter.has(descriptor.get) || isSignalGetter.has(descriptor.get))) {\n\t\t\tmemberStat = null\n\t\t\tcontinue\n\t\t}\n\n\t\t// Handle methods (function-valued properties)\n\t\tif (typeof descriptor.value === 'function' || isAutoAccessor) {\n\t\t\t// Skip base class memoify if a subclass is overriding an auto-accessor memo.\n\t\t\tconst leafmostMemberValue = isAutoAccessor ? descriptor.get : descriptor.value\n\t\t\tif (memberStat && leafmostMemberValue !== memberStat!.value) {\n\t\t\t\tmemberStat = null\n\t\t\t\tcontinue\n\t\t\t}\n\n\t\t\tconst fn = isAutoAccessor ? descriptor.get?.call(obj) : descriptor.value\n\n\t\t\tif (typeof fn !== 'function') {\n\t\t\t\t// Throw in decorator mode only.\n\t\t\t\tif (memberStat) {\n\t\t\t\t\tmemberStat = null\n\t\t\t\t\tthrow new Error(`memo value for \"${String(key)}\" is not a function: ${fn}`)\n\t\t\t\t}\n\t\t\t\t// Otherwise just skip non-function properties (f.e. using memoify(obj) directly on a plain object, without decorators).\n\t\t\t\telse continue\n\t\t\t}\n\n\t\t\tconst name = fn.name || String(key)\n\t\t\tlet value\n\n\t\t\t// Readonly memo: arity 0\n\t\t\tif (fn.length === 0) {\n\t\t\t\tconst get = createMemo(() => fn.call(obj))\n\n\t\t\t\tvalue = (val = Undefined) => {\n\t\t\t\t\tif (val === Undefined) return get()\n\t\t\t\t\tthrow new Error(`Cannot set readonly memoized method \"${String(key)}\".`)\n\t\t\t\t}\n\t\t\t}\n\t\t\t// Writable memo: arity > 0\n\t\t\telse {\n\t\t\t\tconst [get, set] = createWritableMemo(() => fn.call(obj))\n\n\t\t\t\tvalue = (val: unknown = Undefined) => {\n\t\t\t\t\tif (val === Undefined) return get()\n\t\t\t\t\tset(typeof val === 'function' ? () => val : val)\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tObject.defineProperty(value, 'name', {value: name, configurable: true})\n\t\t\tObject.defineProperty(obj, key, {value, configurable: true, enumerable: descriptor.enumerable})\n\t\t\tisMemoGetter.add(value)\n\t\t}\n\n\t\t// Handle accessors\n\t\telse if (descriptor.get || descriptor.set) {\n\t\t\tif (!descriptor.get) {\n\t\t\t\tmemberStat = null\n\t\t\t\tthrow new Error(`Cannot memoify accessor \"${String(key)}\" without a getter.`)\n\t\t\t}\n\t\t\tlet get\n\t\t\tlet set: ((val: unknown) => void) | undefined\n\n\t\t\t// Readonly memo: getter only\n\t\t\tif (!descriptor.set) get = createMemo(() => descriptor.get!.call(obj))\n\t\t\t// Writable memo: getter and setter\n\t\t\telse [get, set] = createWritableMemo(() => descriptor.get!.call(obj))\n\n\t\t\tObject.defineProperty(get, 'name', {value: String(key), configurable: true})\n\t\t\tif (set) Object.defineProperty(set, 'name', {value: String(key), configurable: true})\n\t\t\tObject.defineProperty(obj, key, {\n\t\t\t\tget,\n\t\t\t\tset: set && (val => (typeof val === 'function' ? set(() => val) : set(val))),\n\t\t\t\tconfigurable: true,\n\t\t\t\tenumerable: descriptor.enumerable,\n\t\t\t})\n\t\t\tisMemoGetter.add(get)\n\t\t} else {\n\t\t\t// Throw in decorator mode only.\n\t\t\tif (memberStat) {\n\t\t\t\tmemberStat = null\n\t\t\t\tthrow new Error(`memo value for \"${String(key)}\" is not a function: ${descriptor.value}`)\n\t\t\t}\n\t\t}\n\t}\n\n\tmemberStat = null\n\n\treturn obj\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/signals/signalify.d.ts.map b/dist/signals/signalify.d.ts.map index 7af0c3d..df8e1b2 100644 --- a/dist/signals/signalify.d.ts.map +++ b/dist/signals/signalify.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"signalify.d.ts","sourceRoot":"","sources":["../../src/signals/signalify.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAA;AAKnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AACzE,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAC7E,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,CAAA;AAiCzG,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,WAE7E;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,QAGhF;AAED,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACtD,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAC9B,UAAU,EAAE,OAAO,EACnB,sBAAsB,UAAQ,GAC5B,IAAI,CAkEN;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,2BAI9G"} \ No newline at end of file +{"version":3,"file":"signalify.d.ts","sourceRoot":"","sources":["../../src/signals/signalify.ts"],"names":[],"mappings":"AAGA,OAAO,EAAuB,KAAK,cAAc,EAAC,MAAM,2BAA2B,CAAA;AAKnF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqDG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AACzE,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAC7E,2EAA2E;AAC3E,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,CAAC,EAAE,YAAY,EAAE,OAAO,CAAC,EAAE,GAAG,CAAC,CAAA;AAiCzG,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,WAE7E;AAED,wBAAgB,yBAAyB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,QAGhF;AAED,wBAAgB,sBAAsB,CAAC,CAAC,SAAS,MAAM,EACtD,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAC9B,UAAU,EAAE,OAAO,EACnB,sBAAsB,UAAQ,GAC5B,IAAI,CAsEN;AAED,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,cAAc,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,OAAO,2BAI9G"} \ No newline at end of file diff --git a/dist/signals/signalify.js b/dist/signals/signalify.js index e7b3deb..603eb60 100644 --- a/dist/signals/signalify.js +++ b/dist/signals/signalify.js @@ -1,5 +1,5 @@ import { getInheritedDescriptor } from 'lowclass/dist/getInheritedDescriptor.js'; -import { $PROXY, untrack } from 'solid-js'; +import { $PROXY, batch, untrack } from 'solid-js'; import { createSignalFunction } from './createSignalFunction.js'; import { isMemoGetter, isSignalGetter } from '../_state.js'; @@ -135,10 +135,14 @@ export function createSignalAccessor__(obj, prop, initialVal, skipFunctionProper return getSignal__(this, signalStorage, initialVal)(); }, set: isAccessor ? function (newValue) { - originalSet.call(this, newValue); - trackPropSetAtLeastOnce__(this, prop); - const s = getSignal__(this, signalStorage, initialVal); - s(typeof newValue === 'function' ? () => newValue : newValue); + // batch, for example in case setter calls super setter, to + // avoid multiple effect runs on a single property set. + batch(() => { + originalSet.call(this, newValue); + trackPropSetAtLeastOnce__(this, prop); + const s = getSignal__(this, signalStorage, initialVal); + s(typeof newValue === 'function' ? () => newValue : newValue); + }); } : function (newValue) { trackPropSetAtLeastOnce__(this, prop); const s = getSignal__(this, signalStorage, initialVal); diff --git a/dist/signals/signalify.js.map b/dist/signals/signalify.js.map index 3b8a4ab..eac3bdd 100644 --- a/dist/signals/signalify.js.map +++ b/dist/signals/signalify.js.map @@ -1 +1 @@ -{"version":3,"file":"signalify.js","names":["getInheritedDescriptor","$PROXY","untrack","createSignalFunction","isMemoGetter","isSignalGetter","signalify","obj","props","proxy","skipFunctionProperties","length","_props","Object","keys","concat","getOwnPropertySymbols","prop","isTuple","Array","isArray","_prop","initialValue","createSignalAccessor__","propsSetAtLeastOnce","WeakMap","isPropSetAtLeastOnce__","instance","get","has","trackPropSetAtLeastOnce__","set","Set","add","initialVal","descriptor","originalGet","originalSet","isAccessor","value","writable","warnNotWritable","signalStorage","newDescriptor","configurable","enumerable","getSignal__","call","newValue","s","defineProperty","storage","equals","console","warn","String"],"sources":["../../src/signals/signalify.ts"],"sourcesContent":["import {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js'\nimport {$PROXY, untrack} from 'solid-js'\nimport type {PropKey} from '../decorators/types.js'\nimport {createSignalFunction, type SignalFunction} from './createSignalFunction.js'\nimport {isMemoGetter, isSignalGetter} from '../_state.js'\n\ntype AnyObject = Record\n\n/**\n * Convert properties on an object into Solid signal-backed properties.\n *\n * There are two ways to use this:\n *\n * 1. Define which properties to convert to signal-backed properties by\n * providing property names as trailing arguments. Properties that are\n * function-valued (methods) are included as values of the signal properties.\n * 2. If no property names are provided, all non-function-valued properties on\n * the object will be automatically converted to signal-backed properties.\n *\n * If any property is already memoified with `memoify()`, or already signalified\n * with `signalify()`, it will be skipped.\n *\n * Example with a class:\n *\n * ```js\n * import {signalify} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * class Counter {\n * count = 0\n *\n * constructor() {\n * signalify(this, 'count')\n * setInterval(() => this.count++, 1000)\n * }\n * }\n *\n * const counter = new Counter\n *\n * createEffect(() => {\n * console.log('count:', counter.count)\n * })\n * ```\n *\n * Example with a plain object:\n *\n * ```js\n * import {signalify} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * const counter = {\n * count: 0\n * }\n *\n * signalify(counter, 'count')\n * setInterval(() => counter.count++, 1000)\n *\n * createEffect(() => {\n * console.log('count:', counter.count)\n * })\n * ```\n */\nexport function signalify(obj: T): T\nexport function signalify(obj: T, ...props: (keyof T)[]): T\n/** This overload is for initial value support for downstream use cases. */\nexport function signalify(obj: T, ...props: [key: keyof T, initialValue: unknown][]): T\nexport function signalify(obj: AnyObject, ...props: [key: PropertyKey, initialValue: unknown][] | PropertyKey[]) {\n\t// Special case for Solid proxies: if the object is already a solid proxy,\n\t// all properties are already reactive, no need to signalify.\n\t// @ts-expect-error special indexed access\n\tconst proxy = obj[$PROXY] as T\n\tif (proxy) return obj\n\n\tconst skipFunctionProperties = props.length === 0\n\n\tconst _props = props.length ? props : (Object.keys(obj) as PropKey[]).concat(Object.getOwnPropertySymbols(obj))\n\n\t// Use `untrack` here to be extra safe the initial value doesn't count as a\n\t// dependency and cause a reactivity loop.\n\tfor (const prop of _props) {\n\t\tconst isTuple = Array.isArray(prop)\n\t\t// We cast from PropertyKey to PropKey because keys can't actually be number, only string | symbol.\n\t\tconst _prop = (isTuple ? prop[0] : prop) as PropKey\n\t\tconst initialValue = isTuple ? prop[1] : untrack(() => obj[_prop])\n\n\t\tcreateSignalAccessor__(obj, _prop, initialValue, skipFunctionProperties)\n\t}\n\n\treturn obj\n}\n\n// propsSetAtLeastOnce is a Set that tracks which reactive properties have been\n// set at least once.\nconst propsSetAtLeastOnce = new WeakMap>()\n\n// @lume/element uses this to detect if a reactive prop has been set, and if so\n// will not overwrite the value with any pre-existing value from custom element\n// pre-upgrade.\nexport function isPropSetAtLeastOnce__(instance: object, prop: string | symbol) {\n\treturn !!propsSetAtLeastOnce.get(instance)?.has(prop)\n}\n\nexport function trackPropSetAtLeastOnce__(instance: object, prop: string | symbol) {\n\tif (!propsSetAtLeastOnce.has(instance)) propsSetAtLeastOnce.set(instance, new Set())\n\tpropsSetAtLeastOnce.get(instance)!.add(prop)\n}\n\nexport function createSignalAccessor__(\n\tobj: T,\n\tprop: Exclude,\n\tinitialVal: unknown,\n\tskipFunctionProperties = false,\n): void {\n\tlet descriptor: PropertyDescriptor | undefined = getInheritedDescriptor(obj, prop)\n\n\tlet originalGet: (() => any) | undefined\n\tlet originalSet: ((v: any) => void) | undefined\n\tconst isAccessor = !!(descriptor?.get || descriptor?.set)\n\n\tif (descriptor) {\n\t\tif (skipFunctionProperties && typeof descriptor.value === 'function') return\n\n\t\toriginalGet = descriptor.get\n\t\toriginalSet = descriptor.set\n\n\t\t// If the original getter is already a signal getter, skip re-signalifying.\n\t\tif (originalGet && isSignalGetter.has(originalGet)) return\n\n\t\t// If the original getter is already a memo getter, skip signalifying.\n\t\tif (originalGet && isMemoGetter.has(originalGet)) return\n\n\t\t// Signals require both getter and setter to work properly.\n\t\tif (isAccessor && !(originalGet && originalSet)) return\n\n\t\tif (!isAccessor) {\n\t\t\t// No need to make a signal that can't be written to.\n\t\t\tif (!descriptor.writable) return warnNotWritable(prop)\n\n\t\t\t// If there was a value descriptor, trust it as the source of truth\n\t\t\t// for initialVal. For example, if the user class modifies the value\n\t\t\t// after the initializer, it will have a different value than what\n\t\t\t// we tracked from the initializer.\n\t\t\tinitialVal = descriptor.value\n\t\t}\n\t}\n\n\tconst signalStorage = new WeakMap>()\n\n\tconst newDescriptor = {\n\t\tconfigurable: true,\n\t\tenumerable: descriptor?.enumerable,\n\t\tget: isAccessor\n\t\t\t? function (this: object): unknown {\n\t\t\t\t\tgetSignal__(this, signalStorage, initialVal)()\n\t\t\t\t\treturn originalGet!.call(this)\n\t\t\t }\n\t\t\t: function (this: object): unknown {\n\t\t\t\t\treturn getSignal__(this, signalStorage, initialVal)()\n\t\t\t },\n\t\tset: isAccessor\n\t\t\t? function (this: object, newValue: unknown) {\n\t\t\t\t\toriginalSet!.call(this, newValue)\n\t\t\t\t\ttrackPropSetAtLeastOnce__(this, prop)\n\n\t\t\t\t\tconst s = getSignal__(this, signalStorage, initialVal)\n\t\t\t\t\ts(typeof newValue === 'function' ? () => newValue : newValue)\n\t\t\t }\n\t\t\t: function (this: object, newValue: unknown) {\n\t\t\t\t\ttrackPropSetAtLeastOnce__(this, prop)\n\n\t\t\t\t\tconst s = getSignal__(this, signalStorage, initialVal)\n\t\t\t\t\ts(typeof newValue === 'function' ? () => newValue : newValue)\n\t\t\t },\n\t}\n\n\tisSignalGetter.add(newDescriptor.get!)\n\n\tObject.defineProperty(obj, prop, newDescriptor)\n}\n\nexport function getSignal__(obj: object, storage: WeakMap>, initialVal: unknown) {\n\tlet s = storage.get(obj)\n\tif (!s) storage.set(obj, (s = createSignalFunction(initialVal, {equals: false})))\n\treturn s\n}\n\nfunction warnNotWritable(prop: PropertyKey) {\n\tconsole.warn(\n\t\t`The \\`@signal\\` decorator was used on a property named \"${String(\n\t\t\tprop,\n\t\t)}\" that is not writable. Reactivity is not enabled for non-writable properties.`,\n\t)\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"signalify.js","names":["getInheritedDescriptor","$PROXY","batch","untrack","createSignalFunction","isMemoGetter","isSignalGetter","signalify","obj","props","proxy","skipFunctionProperties","length","_props","Object","keys","concat","getOwnPropertySymbols","prop","isTuple","Array","isArray","_prop","initialValue","createSignalAccessor__","propsSetAtLeastOnce","WeakMap","isPropSetAtLeastOnce__","instance","get","has","trackPropSetAtLeastOnce__","set","Set","add","initialVal","descriptor","originalGet","originalSet","isAccessor","value","writable","warnNotWritable","signalStorage","newDescriptor","configurable","enumerable","getSignal__","call","newValue","s","defineProperty","storage","equals","console","warn","String"],"sources":["../../src/signals/signalify.ts"],"sourcesContent":["import {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js'\nimport {$PROXY, batch, untrack} from 'solid-js'\nimport type {PropKey} from '../decorators/types.js'\nimport {createSignalFunction, type SignalFunction} from './createSignalFunction.js'\nimport {isMemoGetter, isSignalGetter} from '../_state.js'\n\ntype AnyObject = Record\n\n/**\n * Convert properties on an object into Solid signal-backed properties.\n *\n * There are two ways to use this:\n *\n * 1. Define which properties to convert to signal-backed properties by\n * providing property names as trailing arguments. Properties that are\n * function-valued (methods) are included as values of the signal properties.\n * 2. If no property names are provided, all non-function-valued properties on\n * the object will be automatically converted to signal-backed properties.\n *\n * If any property is already memoified with `memoify()`, or already signalified\n * with `signalify()`, it will be skipped.\n *\n * Example with a class:\n *\n * ```js\n * import {signalify} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * class Counter {\n * count = 0\n *\n * constructor() {\n * signalify(this, 'count')\n * setInterval(() => this.count++, 1000)\n * }\n * }\n *\n * const counter = new Counter\n *\n * createEffect(() => {\n * console.log('count:', counter.count)\n * })\n * ```\n *\n * Example with a plain object:\n *\n * ```js\n * import {signalify} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * const counter = {\n * count: 0\n * }\n *\n * signalify(counter, 'count')\n * setInterval(() => counter.count++, 1000)\n *\n * createEffect(() => {\n * console.log('count:', counter.count)\n * })\n * ```\n */\nexport function signalify(obj: T): T\nexport function signalify(obj: T, ...props: (keyof T)[]): T\n/** This overload is for initial value support for downstream use cases. */\nexport function signalify(obj: T, ...props: [key: keyof T, initialValue: unknown][]): T\nexport function signalify(obj: AnyObject, ...props: [key: PropertyKey, initialValue: unknown][] | PropertyKey[]) {\n\t// Special case for Solid proxies: if the object is already a solid proxy,\n\t// all properties are already reactive, no need to signalify.\n\t// @ts-expect-error special indexed access\n\tconst proxy = obj[$PROXY] as T\n\tif (proxy) return obj\n\n\tconst skipFunctionProperties = props.length === 0\n\n\tconst _props = props.length ? props : (Object.keys(obj) as PropKey[]).concat(Object.getOwnPropertySymbols(obj))\n\n\t// Use `untrack` here to be extra safe the initial value doesn't count as a\n\t// dependency and cause a reactivity loop.\n\tfor (const prop of _props) {\n\t\tconst isTuple = Array.isArray(prop)\n\t\t// We cast from PropertyKey to PropKey because keys can't actually be number, only string | symbol.\n\t\tconst _prop = (isTuple ? prop[0] : prop) as PropKey\n\t\tconst initialValue = isTuple ? prop[1] : untrack(() => obj[_prop])\n\n\t\tcreateSignalAccessor__(obj, _prop, initialValue, skipFunctionProperties)\n\t}\n\n\treturn obj\n}\n\n// propsSetAtLeastOnce is a Set that tracks which reactive properties have been\n// set at least once.\nconst propsSetAtLeastOnce = new WeakMap>()\n\n// @lume/element uses this to detect if a reactive prop has been set, and if so\n// will not overwrite the value with any pre-existing value from custom element\n// pre-upgrade.\nexport function isPropSetAtLeastOnce__(instance: object, prop: string | symbol) {\n\treturn !!propsSetAtLeastOnce.get(instance)?.has(prop)\n}\n\nexport function trackPropSetAtLeastOnce__(instance: object, prop: string | symbol) {\n\tif (!propsSetAtLeastOnce.has(instance)) propsSetAtLeastOnce.set(instance, new Set())\n\tpropsSetAtLeastOnce.get(instance)!.add(prop)\n}\n\nexport function createSignalAccessor__(\n\tobj: T,\n\tprop: Exclude,\n\tinitialVal: unknown,\n\tskipFunctionProperties = false,\n): void {\n\tlet descriptor: PropertyDescriptor | undefined = getInheritedDescriptor(obj, prop)\n\n\tlet originalGet: (() => any) | undefined\n\tlet originalSet: ((v: any) => void) | undefined\n\tconst isAccessor = !!(descriptor?.get || descriptor?.set)\n\n\tif (descriptor) {\n\t\tif (skipFunctionProperties && typeof descriptor.value === 'function') return\n\n\t\toriginalGet = descriptor.get\n\t\toriginalSet = descriptor.set\n\n\t\t// If the original getter is already a signal getter, skip re-signalifying.\n\t\tif (originalGet && isSignalGetter.has(originalGet)) return\n\n\t\t// If the original getter is already a memo getter, skip signalifying.\n\t\tif (originalGet && isMemoGetter.has(originalGet)) return\n\n\t\t// Signals require both getter and setter to work properly.\n\t\tif (isAccessor && !(originalGet && originalSet)) return\n\n\t\tif (!isAccessor) {\n\t\t\t// No need to make a signal that can't be written to.\n\t\t\tif (!descriptor.writable) return warnNotWritable(prop)\n\n\t\t\t// If there was a value descriptor, trust it as the source of truth\n\t\t\t// for initialVal. For example, if the user class modifies the value\n\t\t\t// after the initializer, it will have a different value than what\n\t\t\t// we tracked from the initializer.\n\t\t\tinitialVal = descriptor.value\n\t\t}\n\t}\n\n\tconst signalStorage = new WeakMap>()\n\n\tconst newDescriptor = {\n\t\tconfigurable: true,\n\t\tenumerable: descriptor?.enumerable,\n\t\tget: isAccessor\n\t\t\t? function (this: object): unknown {\n\t\t\t\t\tgetSignal__(this, signalStorage, initialVal)()\n\t\t\t\t\treturn originalGet!.call(this)\n\t\t\t }\n\t\t\t: function (this: object): unknown {\n\t\t\t\t\treturn getSignal__(this, signalStorage, initialVal)()\n\t\t\t },\n\t\tset: isAccessor\n\t\t\t? function (this: object, newValue: unknown) {\n\t\t\t\t\t// batch, for example in case setter calls super setter, to\n\t\t\t\t\t// avoid multiple effect runs on a single property set.\n\t\t\t\t\tbatch(() => {\n\t\t\t\t\t\toriginalSet!.call(this, newValue)\n\t\t\t\t\t\ttrackPropSetAtLeastOnce__(this, prop)\n\n\t\t\t\t\t\tconst s = getSignal__(this, signalStorage, initialVal)\n\t\t\t\t\t\ts(typeof newValue === 'function' ? () => newValue : newValue)\n\t\t\t\t\t})\n\t\t\t }\n\t\t\t: function (this: object, newValue: unknown) {\n\t\t\t\t\ttrackPropSetAtLeastOnce__(this, prop)\n\n\t\t\t\t\tconst s = getSignal__(this, signalStorage, initialVal)\n\t\t\t\t\ts(typeof newValue === 'function' ? () => newValue : newValue)\n\t\t\t },\n\t}\n\n\tisSignalGetter.add(newDescriptor.get!)\n\n\tObject.defineProperty(obj, prop, newDescriptor)\n}\n\nexport function getSignal__(obj: object, storage: WeakMap>, initialVal: unknown) {\n\tlet s = storage.get(obj)\n\tif (!s) storage.set(obj, (s = createSignalFunction(initialVal, {equals: false})))\n\treturn s\n}\n\nfunction warnNotWritable(prop: PropertyKey) {\n\tconsole.warn(\n\t\t`The \\`@signal\\` decorator was used on a property named \"${String(\n\t\t\tprop,\n\t\t)}\" that is not writable. Reactivity is not enabled for non-writable properties.`,\n\t)\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/signals/signalify.test.js b/dist/signals/signalify.test.js index e13c45b..1652496 100644 --- a/dist/signals/signalify.test.js +++ b/dist/signals/signalify.test.js @@ -13,8 +13,8 @@ describe('classy-solid', () => { expect(obj).toBe(obj2); obj = createMutable({ n: 123 - }); - obj2 = signalify(obj, 'n'); + }); // Returns a Proxy wrapping the original object + obj2 = signalify(obj, 'n'); // Should return early with the same Proxy expect(obj).toBe(obj2); }); it('skips function properties in automatic mode', () => { diff --git a/dist/signals/signalify.test.js.map b/dist/signals/signalify.test.js.map index d17fd58..9cda5c0 100644 --- a/dist/signals/signalify.test.js.map +++ b/dist/signals/signalify.test.js.map @@ -1 +1 @@ -{"version":3,"file":"signalify.test.js","names":["createMutable","signalify","testButterflyProps","createEffect","untrack","memoify","describe","it","obj","n","obj2","expect","toBe","sum","result","Object","getOwnPropertyDescriptor","value","get","foo","_v","before","after","butterfly","colors","_wingSize","wingSize","s","b","test","signalifyInitially","Butterfly","constructor","b2","prototype","Foo","amount","Bar","double","count","a","name","assign","create","countA","countB"],"sources":["../../src/signals/signalify.test.ts"],"sourcesContent":["import {createMutable} from 'solid-js/store'\nimport {signalify} from './signalify.js'\nimport {testButterflyProps} from '../index.test.js'\nimport {createEffect, untrack} from 'solid-js'\nimport {memoify} from './memoify.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('signalify()', () => {\n\t\tit('returns the same object that was passed in', () => {\n\t\t\tlet obj = {n: 123}\n\t\t\tlet obj2 = signalify(obj, 'n')\n\t\t\texpect(obj).toBe(obj2)\n\n\t\t\tobj = createMutable({n: 123})\n\t\t\tobj2 = signalify(obj, 'n')\n\t\t\texpect(obj).toBe(obj2)\n\t\t})\n\n\t\tit('skips function properties in automatic mode', () => {\n\t\t\tconst obj = {\n\t\t\t\tsum() {\n\t\t\t\t\treturn 1\n\t\t\t\t},\n\t\t\t}\n\t\t\tconst result = signalify(obj)\n\t\t\texpect(result.sum()).toBe(1)\n\t\t\texpect(typeof Object.getOwnPropertyDescriptor(result, 'sum')?.value).toBe('function')\n\t\t})\n\n\t\tit('does not skip function properties in explicit mode', () => {\n\t\t\tconst obj = {\n\t\t\t\tsum() {\n\t\t\t\t\treturn 1\n\t\t\t\t},\n\t\t\t}\n\t\t\tconst result = signalify(obj, 'sum')\n\t\t\texpect(result.sum()).toBe(1)\n\t\t\texpect(typeof Object.getOwnPropertyDescriptor(result, 'sum')?.value === 'undefined').toBe(true)\n\t\t\texpect(typeof Object.getOwnPropertyDescriptor(result, 'sum')?.get).toBe('function')\n\t\t})\n\n\t\tit('skips properties already memoified or signalified', () => {\n\t\t\tconst obj = {\n\t\t\t\tget foo() {\n\t\t\t\t\treturn 1\n\t\t\t\t},\n\t\t\t\tset foo(_v) {},\n\t\t\t}\n\t\t\tmemoify(obj, 'foo')\n\t\t\tconst before = Object.getOwnPropertyDescriptor(obj, 'foo')?.get\n\t\t\tsignalify(obj, 'foo')\n\t\t\tconst after = Object.getOwnPropertyDescriptor(obj, 'foo')?.get\n\t\t\texpect(before).toBe(after)\n\t\t})\n\n\t\tdescribe('making objects reactive with signalify()', () => {\n\t\t\tit('', () => {\n\t\t\t\tconst butterfly = {\n\t\t\t\t\tcolors: 3,\n\n\t\t\t\t\t_wingSize: 2,\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tconst b = signalify(butterfly, 'colors', 'wingSize')\n\n\t\t\t\ttestButterflyProps(b)\n\n\t\t\t\t// quick type check:\n\t\t\t\t// @ts-expect-error \"foo\" is not a property on butterfly\n\t\t\t\tsignalify(butterfly, 'colors', 'wingSize', 'foo')\n\t\t\t})\n\n\t\t\tit('is not tracked inside of an effect to prevent loops', () => {\n\t\t\t\ttest(true)\n\t\t\t\ttest(false)\n\n\t\t\t\tfunction test(signalifyInitially: boolean) {\n\t\t\t\t\t// Library author provides obj\n\t\t\t\t\tconst obj = {n: 123}\n\t\t\t\t\tif (signalifyInitially) signalify(obj, 'n') // library author might signalify obj.n\n\n\t\t\t\t\t// User code:\n\t\t\t\t\tcreateEffect(() => {\n\t\t\t\t\t\t// o.n may or may not already be signalified, user does not know, but they want to be sure they can react to its changes.\n\t\t\t\t\t\tsignalify(obj, 'n')\n\n\t\t\t\t\t\tobj.n = 123 // does not make an infinite loop\n\n\t\t\t\t\t\t// A deeper effect will be reading the property.\n\t\t\t\t\t\tcreateEffect(() => {\n\t\t\t\t\t\t\tobj.n\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\n\t\t\t\t\t// No expectations in this test, the test passes if a maximum\n\t\t\t\t\t// callstack size error (infinite loop) does not happen.\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\n\t\tdescribe('making reactive classes with signalify instead of with decorators', () => {\n\t\t\tit('makes class fields reactive, not using any decorators', () => {\n\t\t\t\tclass Butterfly {\n\t\t\t\t\tcolors = 3\n\n\t\t\t\t\t#wingSize = 2\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this.#wingSize\n\t\t\t\t\t}\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis.#wingSize = s\n\t\t\t\t\t}\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst b = new Butterfly()\n\n\t\t\t\ttestButterflyProps(b)\n\n\t\t\t\t// quick type check:\n\t\t\t\tconst b2 = new Butterfly()\n\t\t\t\t// @ts-expect-error \"foo\" is not a property on Butterfly\n\t\t\t\tsignalify(b2, 'colors', 'wingSize', 'foo')\n\t\t\t})\n\n\t\t\tit('makes constructor properties reactive, not using any decorators', () => {\n\t\t\t\tclass Butterfly {\n\t\t\t\t\tdeclare colors: number\n\n\t\t\t\t\t#wingSize: number\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this.#wingSize\n\t\t\t\t\t}\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis.#wingSize = s\n\t\t\t\t\t}\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tthis.colors = 3\n\t\t\t\t\t\tthis.#wingSize = 2\n\n\t\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst b = new Butterfly()\n\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('works with a function-style class, with signalify in the constructor', () => {\n\t\t\t\tfunction Butterfly() {\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tthis.colors = 3\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tthis._wingSize = 2\n\n\t\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t}\n\n\t\t\t\tButterfly.prototype = {\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// @ts-ignore\n\t\t\t\tconst b = new Butterfly()\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('works with a function-style class, with properties on the prototype, and signalify in constructor', () => {\n\t\t\t\tfunction Butterfly() {\n\t\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t}\n\n\t\t\t\tButterfly.prototype = {\n\t\t\t\t\tcolors: 3,\n\t\t\t\t\t_wingSize: 2,\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\tconst b = new Butterfly()\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('can be used on a function-style class, with properties on the prototype, and signalify on the prototype', () => {\n\t\t\t\tfunction Butterfly() {}\n\n\t\t\t\tButterfly.prototype = {\n\t\t\t\t\tcolors: 3,\n\t\t\t\t\t_wingSize: 2,\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tsignalify(Butterfly.prototype, 'colors', 'wingSize')\n\n\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\tconst b = new Butterfly()\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('can be used on a function-style class, with properties in the constructor, and signalify on the prototype', () => {\n\t\t\t\tfunction Butterfly() {\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tthis.colors = 3\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tthis._wingSize = 2\n\t\t\t\t}\n\n\t\t\t\tButterfly.prototype = {\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tsignalify(Butterfly.prototype, 'colors', 'wingSize')\n\n\t\t\t\t// @ts-ignore\n\t\t\t\tconst b = new Butterfly()\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('show that signalify causes constructor to be reactive when used manually instead of decorators', () => {\n\t\t\t\tclass Foo {\n\t\t\t\t\tamount = 3\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsignalify(this, 'amount')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Bar extends Foo {\n\t\t\t\t\tdouble = 0\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsuper()\n\t\t\t\t\t\tsignalify(this, 'double')\n\t\t\t\t\t\tthis.double = this.amount * 2 // this tracks access of .amount\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet count = 0\n\t\t\t\tlet b!: Bar\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tb = new Bar() // tracks .amount\n\t\t\t\t\tcount++\n\t\t\t\t})\n\n\t\t\t\texpect(count).toBe(1)\n\n\t\t\t\tb.amount = 4 // triggers\n\n\t\t\t\texpect(count).toBe(2)\n\t\t\t})\n\n\t\t\tit('show how to manually untrack constructors when not using decorators', () => {\n\t\t\t\tclass Foo {\n\t\t\t\t\tamount = 3\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsignalify(this, 'amount')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Bar extends Foo {\n\t\t\t\t\tdouble = 0\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsuper()\n\t\t\t\t\t\tsignalify(this, 'double')\n\n\t\t\t\t\t\tuntrack(() => {\n\t\t\t\t\t\t\tthis.double = this.amount * 2\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet count = 0\n\t\t\t\tlet b!: Bar\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tb = new Bar() // does not track .amount\n\t\t\t\t\tcount++\n\t\t\t\t})\n\n\t\t\t\texpect(count).toBe(1)\n\n\t\t\t\tb.amount = 4 // will not trigger\n\n\t\t\t\texpect(count).toBe(1)\n\t\t\t})\n\t\t})\n\n\t\tit('creates signal storage per descriptor+object pair, not per descriptor', () => {\n\t\t\t// This ensures we don't accidentally share a signal with multiple\n\t\t\t// objects. For example, we don't want a single signal per descriptor\n\t\t\t// because if the descriptor is on a prototype object, then that single\n\t\t\t// signal will erroneously be used by all objects extending from that\n\t\t\t// prototype.\n\n\t\t\tconst a = signalify({foo: 0, name: 'a'}, 'foo')\n\t\t\tconst b = Object.assign(Object.create(a) as typeof a, {name: 'b'})\n\n\t\t\texpect(a.foo).toBe(0)\n\t\t\texpect(b.foo).toBe(0)\n\n\t\t\tlet countA = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\ta.foo\n\t\t\t\tcountA++\n\t\t\t})\n\n\t\t\tlet countB = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tb.foo\n\t\t\t\tcountB++\n\t\t\t})\n\n\t\t\texpect(countA).toBe(1)\n\t\t\texpect(countB).toBe(1)\n\n\t\t\ta.foo++\n\n\t\t\texpect(a.foo).toBe(1)\n\t\t\texpect(countA).toBe(2)\n\n\t\t\t// ensure that updating a's foo property did not update b's foo\n\t\t\t// property or trigger b's effect, despite that the property is\n\t\t\t// defined in a single location on the prototype.\n\t\t\t// @ts-ignore\n\t\t\texpect(b.foo).toBe(0)\n\t\t\texpect(countB).toBe(1)\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"signalify.test.js","names":["createMutable","signalify","testButterflyProps","createEffect","untrack","memoify","describe","it","obj","n","obj2","expect","toBe","sum","result","Object","getOwnPropertyDescriptor","value","get","foo","_v","before","after","butterfly","colors","_wingSize","wingSize","s","b","test","signalifyInitially","Butterfly","constructor","b2","prototype","Foo","amount","Bar","double","count","a","name","assign","create","countA","countB"],"sources":["../../src/signals/signalify.test.ts"],"sourcesContent":["import {createMutable} from 'solid-js/store'\nimport {signalify} from './signalify.js'\nimport {testButterflyProps} from '../index.test.js'\nimport {createEffect, untrack} from 'solid-js'\nimport {memoify} from './memoify.js'\n\ndescribe('classy-solid', () => {\n\tdescribe('signalify()', () => {\n\t\tit('returns the same object that was passed in', () => {\n\t\t\tlet obj = {n: 123}\n\t\t\tlet obj2 = signalify(obj, 'n')\n\t\t\texpect(obj).toBe(obj2)\n\n\t\t\tobj = createMutable({n: 123}) // Returns a Proxy wrapping the original object\n\t\t\tobj2 = signalify(obj, 'n') // Should return early with the same Proxy\n\t\t\texpect(obj).toBe(obj2)\n\t\t})\n\n\t\tit('skips function properties in automatic mode', () => {\n\t\t\tconst obj = {\n\t\t\t\tsum() {\n\t\t\t\t\treturn 1\n\t\t\t\t},\n\t\t\t}\n\t\t\tconst result = signalify(obj)\n\t\t\texpect(result.sum()).toBe(1)\n\t\t\texpect(typeof Object.getOwnPropertyDescriptor(result, 'sum')?.value).toBe('function')\n\t\t})\n\n\t\tit('does not skip function properties in explicit mode', () => {\n\t\t\tconst obj = {\n\t\t\t\tsum() {\n\t\t\t\t\treturn 1\n\t\t\t\t},\n\t\t\t}\n\t\t\tconst result = signalify(obj, 'sum')\n\t\t\texpect(result.sum()).toBe(1)\n\t\t\texpect(typeof Object.getOwnPropertyDescriptor(result, 'sum')?.value === 'undefined').toBe(true)\n\t\t\texpect(typeof Object.getOwnPropertyDescriptor(result, 'sum')?.get).toBe('function')\n\t\t})\n\n\t\tit('skips properties already memoified or signalified', () => {\n\t\t\tconst obj = {\n\t\t\t\tget foo() {\n\t\t\t\t\treturn 1\n\t\t\t\t},\n\t\t\t\tset foo(_v) {},\n\t\t\t}\n\t\t\tmemoify(obj, 'foo')\n\t\t\tconst before = Object.getOwnPropertyDescriptor(obj, 'foo')?.get\n\t\t\tsignalify(obj, 'foo')\n\t\t\tconst after = Object.getOwnPropertyDescriptor(obj, 'foo')?.get\n\t\t\texpect(before).toBe(after)\n\t\t})\n\n\t\tdescribe('making objects reactive with signalify()', () => {\n\t\t\tit('', () => {\n\t\t\t\tconst butterfly = {\n\t\t\t\t\tcolors: 3,\n\n\t\t\t\t\t_wingSize: 2,\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tconst b = signalify(butterfly, 'colors', 'wingSize')\n\n\t\t\t\ttestButterflyProps(b)\n\n\t\t\t\t// quick type check:\n\t\t\t\t// @ts-expect-error \"foo\" is not a property on butterfly\n\t\t\t\tsignalify(butterfly, 'colors', 'wingSize', 'foo')\n\t\t\t})\n\n\t\t\tit('is not tracked inside of an effect to prevent loops', () => {\n\t\t\t\ttest(true)\n\t\t\t\ttest(false)\n\n\t\t\t\tfunction test(signalifyInitially: boolean) {\n\t\t\t\t\t// Library author provides obj\n\t\t\t\t\tconst obj = {n: 123}\n\t\t\t\t\tif (signalifyInitially) signalify(obj, 'n') // library author might signalify obj.n\n\n\t\t\t\t\t// User code:\n\t\t\t\t\tcreateEffect(() => {\n\t\t\t\t\t\t// o.n may or may not already be signalified, user does not know, but they want to be sure they can react to its changes.\n\t\t\t\t\t\tsignalify(obj, 'n')\n\n\t\t\t\t\t\tobj.n = 123 // does not make an infinite loop\n\n\t\t\t\t\t\t// A deeper effect will be reading the property.\n\t\t\t\t\t\tcreateEffect(() => {\n\t\t\t\t\t\t\tobj.n\n\t\t\t\t\t\t})\n\t\t\t\t\t})\n\n\t\t\t\t\t// No expectations in this test, the test passes if a maximum\n\t\t\t\t\t// callstack size error (infinite loop) does not happen.\n\t\t\t\t}\n\t\t\t})\n\t\t})\n\n\t\tdescribe('making reactive classes with signalify instead of with decorators', () => {\n\t\t\tit('makes class fields reactive, not using any decorators', () => {\n\t\t\t\tclass Butterfly {\n\t\t\t\t\tcolors = 3\n\n\t\t\t\t\t#wingSize = 2\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this.#wingSize\n\t\t\t\t\t}\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis.#wingSize = s\n\t\t\t\t\t}\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst b = new Butterfly()\n\n\t\t\t\ttestButterflyProps(b)\n\n\t\t\t\t// quick type check:\n\t\t\t\tconst b2 = new Butterfly()\n\t\t\t\t// @ts-expect-error \"foo\" is not a property on Butterfly\n\t\t\t\tsignalify(b2, 'colors', 'wingSize', 'foo')\n\t\t\t})\n\n\t\t\tit('makes constructor properties reactive, not using any decorators', () => {\n\t\t\t\tclass Butterfly {\n\t\t\t\t\tdeclare colors: number\n\n\t\t\t\t\t#wingSize: number\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this.#wingSize\n\t\t\t\t\t}\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis.#wingSize = s\n\t\t\t\t\t}\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tthis.colors = 3\n\t\t\t\t\t\tthis.#wingSize = 2\n\n\t\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tconst b = new Butterfly()\n\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('works with a function-style class, with signalify in the constructor', () => {\n\t\t\t\tfunction Butterfly() {\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tthis.colors = 3\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tthis._wingSize = 2\n\n\t\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t}\n\n\t\t\t\tButterfly.prototype = {\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// @ts-ignore\n\t\t\t\tconst b = new Butterfly()\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('works with a function-style class, with properties on the prototype, and signalify in constructor', () => {\n\t\t\t\tfunction Butterfly() {\n\t\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t}\n\n\t\t\t\tButterfly.prototype = {\n\t\t\t\t\tcolors: 3,\n\t\t\t\t\t_wingSize: 2,\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\tconst b = new Butterfly()\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('can be used on a function-style class, with properties on the prototype, and signalify on the prototype', () => {\n\t\t\t\tfunction Butterfly() {}\n\n\t\t\t\tButterfly.prototype = {\n\t\t\t\t\tcolors: 3,\n\t\t\t\t\t_wingSize: 2,\n\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tsignalify(Butterfly.prototype, 'colors', 'wingSize')\n\n\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\tconst b = new Butterfly()\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('can be used on a function-style class, with properties in the constructor, and signalify on the prototype', () => {\n\t\t\t\tfunction Butterfly() {\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tthis.colors = 3\n\t\t\t\t\t// @ts-ignore\n\t\t\t\t\tthis._wingSize = 2\n\t\t\t\t}\n\n\t\t\t\tButterfly.prototype = {\n\t\t\t\t\tget wingSize() {\n\t\t\t\t\t\treturn this._wingSize\n\t\t\t\t\t},\n\t\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\t\tthis._wingSize = s\n\t\t\t\t\t},\n\t\t\t\t}\n\n\t\t\t\tsignalify(Butterfly.prototype, 'colors', 'wingSize')\n\n\t\t\t\t// @ts-ignore\n\t\t\t\tconst b = new Butterfly()\n\t\t\t\ttestButterflyProps(b)\n\t\t\t})\n\n\t\t\tit('show that signalify causes constructor to be reactive when used manually instead of decorators', () => {\n\t\t\t\tclass Foo {\n\t\t\t\t\tamount = 3\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsignalify(this, 'amount')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Bar extends Foo {\n\t\t\t\t\tdouble = 0\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsuper()\n\t\t\t\t\t\tsignalify(this, 'double')\n\t\t\t\t\t\tthis.double = this.amount * 2 // this tracks access of .amount\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet count = 0\n\t\t\t\tlet b!: Bar\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tb = new Bar() // tracks .amount\n\t\t\t\t\tcount++\n\t\t\t\t})\n\n\t\t\t\texpect(count).toBe(1)\n\n\t\t\t\tb.amount = 4 // triggers\n\n\t\t\t\texpect(count).toBe(2)\n\t\t\t})\n\n\t\t\tit('show how to manually untrack constructors when not using decorators', () => {\n\t\t\t\tclass Foo {\n\t\t\t\t\tamount = 3\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsignalify(this, 'amount')\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tclass Bar extends Foo {\n\t\t\t\t\tdouble = 0\n\n\t\t\t\t\tconstructor() {\n\t\t\t\t\t\tsuper()\n\t\t\t\t\t\tsignalify(this, 'double')\n\n\t\t\t\t\t\tuntrack(() => {\n\t\t\t\t\t\t\tthis.double = this.amount * 2\n\t\t\t\t\t\t})\n\t\t\t\t\t}\n\t\t\t\t}\n\n\t\t\t\tlet count = 0\n\t\t\t\tlet b!: Bar\n\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tb = new Bar() // does not track .amount\n\t\t\t\t\tcount++\n\t\t\t\t})\n\n\t\t\t\texpect(count).toBe(1)\n\n\t\t\t\tb.amount = 4 // will not trigger\n\n\t\t\t\texpect(count).toBe(1)\n\t\t\t})\n\t\t})\n\n\t\tit('creates signal storage per descriptor+object pair, not per descriptor', () => {\n\t\t\t// This ensures we don't accidentally share a signal with multiple\n\t\t\t// objects. For example, we don't want a single signal per descriptor\n\t\t\t// because if the descriptor is on a prototype object, then that single\n\t\t\t// signal will erroneously be used by all objects extending from that\n\t\t\t// prototype.\n\n\t\t\tconst a = signalify({foo: 0, name: 'a'}, 'foo')\n\t\t\tconst b = Object.assign(Object.create(a) as typeof a, {name: 'b'})\n\n\t\t\texpect(a.foo).toBe(0)\n\t\t\texpect(b.foo).toBe(0)\n\n\t\t\tlet countA = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\ta.foo\n\t\t\t\tcountA++\n\t\t\t})\n\n\t\t\tlet countB = 0\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tb.foo\n\t\t\t\tcountB++\n\t\t\t})\n\n\t\t\texpect(countA).toBe(1)\n\t\t\texpect(countB).toBe(1)\n\n\t\t\ta.foo++\n\n\t\t\texpect(a.foo).toBe(1)\n\t\t\texpect(countA).toBe(2)\n\n\t\t\t// ensure that updating a's foo property did not update b's foo\n\t\t\t// property or trigger b's effect, despite that the property is\n\t\t\t// defined in a single location on the prototype.\n\t\t\t// @ts-ignore\n\t\t\texpect(b.foo).toBe(0)\n\t\t\texpect(countB).toBe(1)\n\t\t})\n\t})\n})\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/example/index.html b/example/index.html index ac2c189..969b93a 100644 --- a/example/index.html +++ b/example/index.html @@ -5,7 +5,10 @@ "solid-js/web": "/node_modules/solid-js/web/dist/web.js", "solid-js/html": "/node_modules/solid-js/html/dist/html.js", "solid-js/store": "/node_modules/solid-js/store/dist/store.js", - "lowclass": "/node_modules/lowclass/dist/index.js" + "@solid-primitives/memo": "/node_modules/@solid-primitives/memo/dist/index.js", + "@solid-primitives/scheduled": "/node_modules/@solid-primitives/scheduled/dist/index.js", + "@solid-primitives/utils": "/node_modules/@solid-primitives/utils/dist/index.js", + "lowclass/": "/node_modules/lowclass/" } } diff --git a/lume.config.cjs b/lume.config.cjs index 5ab78cc..95a38b7 100644 --- a/lume.config.cjs +++ b/lume.config.cjs @@ -3,7 +3,6 @@ module.exports = { useBabelForTypeScript: true, importMap: { imports: { - lowclass: '/node_modules/lowclass/dist/index.js', 'lowclass/': '/node_modules/lowclass/', 'solid-js': '/node_modules/solid-js/dist/solid.js', 'solid-js/web': '/node_modules/solid-js/web/dist/web.js', diff --git a/src/_state.ts b/src/_state.ts index d874921..de4e206 100644 --- a/src/_state.ts +++ b/src/_state.ts @@ -1,107 +1,180 @@ -import type {PropKey, SignalMetadata, SignalOrMemoType} from './decorators/types.js' -import {memoify} from './signals/memoify.js' +import type { + AnyObject, + MemberStat, + MetadataMembers, + PropKey, + ClassySolidMetadata, + SignalOrMemoType, +} from './decorators/types.js' +import {memoify, setMemoifyMemberStat} from './signals/memoify.js' +import {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js' +import {signalify} from './signals/signalify.js' +import {Effects} from './mixins/Effectful.js' export const isSignalGetter = new WeakSet() export const isMemoGetter = new WeakSet() -const isSorted = new WeakSet() +export function getMembers(metadata: ClassySolidMetadata) { + if (!Object.hasOwn(metadata, 'classySolid_members')) metadata.classySolid_members = [] // we don't extend the array from parent classes + return metadata.classySolid_members! +} + +export function getMemberStat(name: PropKey, type: SignalOrMemoType, members: MetadataMembers) { + const index = members.findIndex(member => member.name === name) + const existingStat = members[index] + const newStat: MemberStat = {type, name, applied: new WeakMap(), finalize: () => {}} + + // replace stat in the array with the latest (f.e. duplicate class members, last one wins) + if (existingStat) members[index] = newStat + else members.push(newStat) + + return newStat +} -const typeOrder = {'signal-field': 0, 'memo-field': 1, 'memo-auto-accessor': 1, 'memo-accessor': 2, 'memo-method': 2} +const isSortedCustom = new WeakSet() -export function getSignalsAndMemos(metadata: SignalMetadata) { - if (!Object.hasOwn(metadata, 'signalFieldsAndMemos')) metadata.signalFieldsAndMemos = [] - return metadata.signalFieldsAndMemos! +// This is the order we want for initializing supported types of members. +const customSortOrder: Record = { + 'signal-field': 0, + 'memo-auto-accessor': 1, + 'memo-accessor': 1, + 'memo-method': 1, + 'effect-auto-accessor': 2, + 'effect-method': 2, } +// This is the EcmaScript decorator extra initializer application order we don't want, for reference. +// Anything in the same group is applied in source order within that group. +// const ecmascriptSortOrder: Record = { +// 'memo-method': 0, +// 'memo-accessor': 0, +// 'effect-method': 0, +// 'signal-field': 1, +// 'memo-auto-accessor': 1, +// 'effect-auto-accessor': 1, +// } + /** - * Sort certain members tracked in metadata in the order of - * - * 1. signal fields - * 2. memo fields - * 3. memo auto-accessors - * 4. memo accessors - * 5. memo methods + * Sort signal, memo, and effect members tracked in metadata in the order of + * our custom `memberSortOrder`. * - * so that any members that are normally initialized *after* - * getters/setters/methods (fields and accessors, such as signal fields, and - * memo fields and auto-accessors) will be initialized before - * getters/setters/methods (memo accessors and methods). + * This is so that any members that are normally initialized *after* + * getters/setters/methods (such as signal fields, and memo fields and + * auto-accessors) will be initialized before getters/setters/methods (memo + * accessors and methods), otherwise they will be initialize *after* + * getters/setters/methods due to EcmaScript decorator application order rules. * - * This ensures proper initialization order which we cannot currently achieve - * with the default ordering of EcmaScript decorators alone. + * See: https://github.com/tc39/proposal-decorators/issues/566 */ -export function sortSignalsMemosInMetadata(metadata: SignalMetadata) { - if (!metadata.signalFieldsAndMemos) return - - if (isSorted.has(metadata)) return - isSorted.add(metadata) +function sortMetadataMembersCustomOrder(members: MetadataMembers) { + // Avoid sorting multiple times (that's why this is called in class + // initializers rather than in the decorator directly). + if (isSortedCustom.has(members)) return + isSortedCustom.add(members) // Sort so that signal fields come first, then memo fields and // auto-accessors, finally memo accessors and methods. - metadata.signalFieldsAndMemos.sort((a, b) => typeOrder[a[1].type] - typeOrder[b[1].type]) + members.sort((a, b) => customSortOrder[a.type] - customSortOrder[b.type]) } -export function getMemberStat(name: PropKey, type: SignalOrMemoType, signalsAndMemos: any[]) { - let stat = signalsAndMemos.find(([key]) => key === name)?.[1] - if (!stat) signalsAndMemos.push([name, (stat = {type, applied: new WeakMap()})]) - return stat +export function signalifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat) { + if (stat.applied.get(obj)) + throw new Error( + `@signal decorated member "${String( + name, + )}" has already been signalified. This can happen if there are duplicated class members.`, + ) + + signalify(obj, [name, /*untrack*/ () => obj[name]]) // untrack in case obj[name] is already a signal (f.e. from a Solid Proxy) + + stat.applied.set(obj, true) } -export function memoifyIfNeeded(obj: object, name: PropKey, stat: any, isAutoAccessor = false) { - if (stat.applied.get(obj)) return - isAutoAccessor ? memoify(obj, true, name as keyof typeof obj) : memoify(obj, name as keyof typeof obj) +export function memoifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat) { + if (stat.applied.get(obj)) + throw new Error( + `@memo decorated member "${String( + name, + )}" has already been memoified. This can happen if there are duplicated class members.`, + ) + + setMemoifyMemberStat(stat) + memoify(obj, name as keyof typeof obj) + stat.applied.set(obj, true) } -/** - * If any signal-fields, memo-fields, or memo-auto-accessors are defined on the - * class (thus sorted before the given memo field), skip memoifying now (true - * return). We'll memoify later after signal fields are initialized. - */ -export function isPriorSignalOrMemoDefined(obj: object, name: PropKey, signalsAndMemos: any[]) { - for (const [key, stat] of signalsAndMemos) { - if ( - (stat.type === 'signal-field' || stat.type === 'memo-field' || stat.type === 'memo-auto-accessor') && - !stat.applied.get(obj) +/** @private internal state */ +export const effects__ = new WeakMap() + +export function effectifyIfNeeded(obj: AnyObject, name: PropKey, stat: MemberStat) { + if (stat.applied.get(obj)) + throw new Error( + `@effect decorated member "${String( + name, + )}" has already been effectified. This can happen if there are duplicated class members.`, ) - return true - if (key === name) break // reached our own memo field, no prior signal-fields or memo-auto-accessors found + + const decoratorValue = stat.value as Function + if (!decoratorValue) throw new Error('not possible') + + const descriptor = getInheritedDescriptor(obj, name)! + const leafmostMemberValue = stat.type === 'effect-auto-accessor' ? descriptor.get : obj[name] + + // Skip base class effectify if a subclass is overriding an effect. + if (leafmostMemberValue !== decoratorValue) return + + const fn = obj[name] + if (typeof fn !== 'function') throw new Error(`@effect decorated member "${String(name)}" is not a function: ${fn}`) + + let effects = effects__.get(obj) + if (!effects) { + // If the object is already an Effects instance, use it directly. + if (obj instanceof Effects) effects__.set(obj, (effects = obj)) + // Otherwise, create a new Effects instance to manage the effects. + else effects__.set(obj, (effects = new Effects())) } - return false + + effects.createEffect(() => fn.call(obj)) + + stat.applied.set(obj, true) } /** - * This finalizes memo initialization for memo accessors and methods that - * were waiting for all signal fields, memo fields, and memo auto-accessors - * to be initialized first. - * - * Basically we ensure that memo initialization happens in this order: - * 1. signal fields - * 2. memo fields - * 3. memo auto-accessors - * 4. memo accessors - * 5. memo methods + * Count number of extra initializers called for the given members array + * per instance, so we know when the last one is called, to finalize all + * members. + */ +const extraInitializersCount = new WeakMap() + +/** + * This finalizes memo initialization for the members tracked, in our custom + * ordering. * * This is important because memos may depend on signals or other memos, and we - * cannot rely on EcmaScript decorator application order alone, since accessor - * and method before field decorators no matter the order in source code. + * cannot rely on EcmaScript decorator order, or extra initializer order alone, + * because accessor and method decorators/initializers run before field + * decorators no matter the order in source code (give or take some details + * regarding auto accessor ordering). * * See: https://github.com/tc39/proposal-decorators/issues/566 */ -export function finalizeMemos(obj: object, stat: any, signalsAndMemos: any[]) { - const last = signalsAndMemos.findLast( - ([_, {type}]) => type === 'signal-field' || type === 'memo-field' || type === 'memo-auto-accessor', - )! - const [, lastStat] = last +export function finalizeMembersIfLast(obj: AnyObject, members: MetadataMembers) { + let count = extraInitializersCount.get(obj) ?? 0 + extraInitializersCount.set(obj, ++count) - if (stat !== lastStat) return + // If this is not the last extra initializer called, return. + if (count !== members.length) return + + extraInitializersCount.set(obj, 0) + + // The last member in EcmaScript decorator extra initializer application + // order has been initialized, so we can now initialize all the members we + // track in our custom order. + sortMetadataMembersCustomOrder(members) // All signal-fields, memo-fields, and memo-auto-accessors have been // initialized. Now initialize memo fields that were waiting for // those to be ready. - for (const [key, stat] of signalsAndMemos) { - if (!(stat.type === 'memo-accessor' || stat.type === 'memo-method') || stat.applied.get(obj)) continue - - memoifyIfNeeded(obj, key, stat) - } + for (const stat of members) stat.finalize?.call(obj) } diff --git a/src/decorators/component.test.ts b/src/decorators/component.test.ts index 80afc0c..fd5cd35 100644 --- a/src/decorators/component.test.ts +++ b/src/decorators/component.test.ts @@ -1,12 +1,13 @@ import html from 'solid-js/html' -import {component} from './component.js' +import {component, type Props} from './component.js' import {render} from 'solid-js/web' -import {reactive} from './reactive.js' import {signal} from './signal.js' import {createSignal} from 'solid-js' import {createSignalFunction} from '../signals/createSignalFunction.js' import {signalify} from '../signals/signalify.js' import {createMutable} from 'solid-js/store' +import {memo} from './memo.js' +import {effect} from './effect.js' describe('classy-solid', () => { describe('@component', () => { @@ -16,6 +17,10 @@ describe('classy-solid', () => { @component class CoolComp { + declare PropTypes: Props + + @signal foo = 0 + onMount() { onMountCalled = true } @@ -24,18 +29,25 @@ describe('classy-solid', () => { onCleanupCalled = true } - template(props: any) { - expect(props.foo).toBe(123) - return html`
hello classes!
` + template(props: this['PropTypes']) { + expect(props.foo).toBe(123) // not recommended to access props this way + + expect(this.foo).toBe(0) // initial value only + + return html`
hello classes! ${() => this.foo}
` } } + // Component classes cannot be instantiated directly, they can only + // be used as Solid components in JSX or html templates. + expect(() => new CoolComp()).toThrow() + const root = document.createElement('div') document.body.append(root) const dispose = render(() => html`<${CoolComp} foo=${123} />`, root) - expect(root.textContent).toBe('hello classes!') + expect(root.textContent).toBe('hello classes! 123') expect(onMountCalled).toBe(true) expect(onCleanupCalled).toBe(false) @@ -43,7 +55,9 @@ describe('classy-solid', () => { root.remove() expect(onCleanupCalled).toBe(true) + }) + it('throws on invalid use', () => { // throws on non-class use expect(() => { class CoolComp { @@ -55,13 +69,47 @@ describe('classy-solid', () => { }).toThrow('component decorator should only be used on a class') }) - it('works in tandem with @reactive and @signal for reactivity', async () => { + it('allows getting a ref to the class instance', () => { @component - @reactive class CoolComp { + coolness = Infinity + template = () => html`
hello refs!
` + } + + const root = document.createElement('div') + document.body.append(root) + + let compRef!: CoolComp + + const dispose = render(() => html`<${CoolComp} ref=${(comp: CoolComp) => (compRef = comp)} />`, root) + + expect(root.textContent).toBe('hello refs!') + expect(compRef instanceof CoolComp).toBe(true) + expect(compRef.coolness).toBe(Infinity) + + dispose() + root.remove() + }) + + it('works in tandem with @signal, @memo, and @effect for reactivity', async () => { + @component + class CoolComp { + declare PropTypes: Props + @signal foo = 0 @signal bar = 0 + @memo get sum() { + return this.foo + this.bar + } + + runs = 0 + result = 0 + @effect logSum() { + this.runs++ + this.result = this.sum + } + template() { return html`
foo: ${() => this.foo}, bar: ${() => this.bar}
` } @@ -73,21 +121,37 @@ describe('classy-solid', () => { const [a, setA] = createSignal(1) const b = createSignalFunction(2) + let compRef!: CoolComp + // FIXME Why do we need `() => b()` instead of just `b` here? Does `html` // check the `length` of the function and do something based on // that? Or does it get passed to a @signal property's setter and // receives the previous value? - const dispose = render(() => html`<${CoolComp} foo=${a} bar=${() => b()} />`, root) + const dispose = render( + () => html` <${CoolComp} ref=${(comp: CoolComp) => (compRef = comp)} foo=${a} bar=${() => b()} /> `, + root, + ) expect(root.textContent).toBe('foo: 1, bar: 2') + expect(compRef.result).toBe(3) + expect(compRef.runs).toBe(2) // 1 initial run with 0 and 0, 1 run from setting foo and bar props setA(3) - b(4) + expect(root.textContent).toBe('foo: 3, bar: 2') + expect(compRef.result).toBe(5) + expect(compRef.runs).toBe(3) + b(4) expect(root.textContent).toBe('foo: 3, bar: 4') + expect(compRef.result).toBe(7) + expect(compRef.runs).toBe(4) dispose() root.remove() + setA(5) + b(6) + expect(compRef.result).toBe(7) // no change after dispose + expect(compRef.runs).toBe(4) // no change after dispose }) it('works without decorators', () => { @@ -129,10 +193,10 @@ describe('classy-solid', () => { root.remove() }) - // FIXME not working, the spread doesn't seem to do anything. - xit('works with reactive spreads', async () => { + // FIXME not working, spread syntax not supported yet in solid-js/html + // TODO unit test using JSX + it.skip('works with reactive spreads', async () => { @component - @reactive class CoolComp { @signal foo = 0 @signal bar = 0 diff --git a/src/decorators/component.ts b/src/decorators/component.ts index a216de4..42a401c 100644 --- a/src/decorators/component.ts +++ b/src/decorators/component.ts @@ -1,5 +1,6 @@ import {Constructor} from 'lowclass/dist/Constructor.js' import {onMount, createEffect, onCleanup, type JSX, $TRACK, createMemo} from 'solid-js' +import './metadata-shim.js' // https://github.com/ryansolid/dom-expressions/pull/122 @@ -17,7 +18,6 @@ interface PossiblyReactiveConstructor {} * * ```js * ⁣@component - * ⁣@reactive * class MyComp { * ⁣@signal last = 'none' * @@ -41,7 +41,10 @@ export function component(Base: T, context?: DecoratorCon const Class = Constructor(Base) - return function (props: Record): JSX.Element { + // Solid only undetstands function components, so we create a wrapper + // function that instantiates the class and hooks up lifecycle methods and + // props. + function classComponentWrapper(props: Record): JSX.Element { const instance = new Class() const keys = createMemo( @@ -60,19 +63,38 @@ export function component(Base: T, context?: DecoratorCon ) createEffect(() => { - for (const prop of keys()) { - createEffect(() => { - // @ts-expect-error - instance[prop] = props[prop] - }) - } + // @ts-expect-error index signature + for (const prop of keys()) createEffect(() => (instance[prop] = props[prop])) }) - if (instance.onMount) onMount(() => instance.onMount?.()) - if (instance.onCleanup) onCleanup(() => instance.onCleanup?.()) + onMount(() => { + instance.onMount?.() + + createEffect(() => { + const ref = props.ref as ((component: PossibleComponent) => void) | undefined + ref?.(instance) + }) + + onCleanup(() => instance.onCleanup?.()) + }) return instance.template?.(props) ?? null } + + Object.defineProperties(classComponentWrapper, { + name: { + value: Class.name, + configurable: true, + }, + [Symbol.hasInstance]: { + value(obj: unknown) { + return obj instanceof Class + }, + configurable: true, + }, + }) + + return classComponentWrapper } declare module 'solid-js' { @@ -91,4 +113,5 @@ declare module 'solid-js' { export type Props = Pick & { children?: JSX.Element + ref?: (component: T) => void } diff --git a/src/decorators/effect.test.ts b/src/decorators/effect.test.ts new file mode 100644 index 0000000..2c05e18 --- /dev/null +++ b/src/decorators/effect.test.ts @@ -0,0 +1,770 @@ +import {createSignal, batch, createRoot, createEffect} from 'solid-js' +import {signal} from './signal.js' +import {memo} from './memo.js' +import {effect, startEffects, stopEffects} from './effect.js' +import {Effects} from '../mixins/Effectful.js' +import {testElementEffects, type MyElement4} from '../index.test.js' + +describe('classy-solid', () => { + describe('@effect decorator', () => { + it('runs a basic method effect with signals using stopEffects', () => { + const [a, setA] = createSignal(1) + let last: number | null = null + let runs = 0 + + class Funkalicious { + @signal b = 2 + + @effect logSum() { + runs++ + last = a() + this.b + } + } + + const fun = new Funkalicious() + expect(last).toBe(1 + 2) + expect(runs).toBe(1) + + setA(5) + expect(last).toBe(5 + 2) + expect(runs).toBe(2) + + fun.b = 10 + expect(last).toBe(5 + 10) + expect(runs).toBe(3) + + stopEffects(fun) + setA(1) + fun.b = 1 + expect(last).toBe(5 + 10) + expect(runs).toBe(3) + + startEffects(fun) + expect(last).toBe(1 + 1) + expect(runs).toBe(4) + + // Ensure no duplicate effects + startEffects(fun) + expect(last).toBe(1 + 1) + expect(runs).toBe(4) + + setA(3) + expect(last).toBe(3 + 1) + expect(runs).toBe(5) + + stopEffects(fun) + setA(10) + fun.b = 20 + expect(last).toBe(3 + 1) + expect(runs).toBe(5) + }) + + it('runs a basic method effect with signals using Effects', () => { + const [a, setA] = createSignal(1) + let last: number | null = null + let runs = 0 + + class Funkalicious extends Effects { + @signal b = 2 + @effect logSum() { + runs++ + last = a() + this.b + } + } + + const fun = new Funkalicious() + expect(last).toBe(3) + expect(runs).toBe(1) + + setA(5) + expect(last).toBe(7) + expect(runs).toBe(2) + + fun.b = 10 + expect(last).toBe(15) + expect(runs).toBe(3) + + fun.stopEffects() + setA(1) + fun.b = 1 + expect(last).toBe(15) + expect(runs).toBe(3) + }) + + it('runs multiple effects independently using Effects', () => { + const [a, setA] = createSignal(1) + let sum1 = 0 + let sum2 = 0 + let runs = 0 + + class Doubler extends Effects { + @signal b = 3 + @effect eff1() { + runs++ + sum1 = a() + this.b + } + @effect accessor eff2 = () => { + runs++ + sum2 = (a() + this.b) * 2 + } + } + + const o = new Doubler() + expect(sum1).toBe(4) + expect(sum2).toBe(8) + expect(runs).toBe(2) + + setA(2) + expect(sum1).toBe(5) + expect(sum2).toBe(10) + expect(runs).toBe(4) + + o.b = 4 + expect(sum1).toBe(6) + expect(sum2).toBe(12) + expect(runs).toBe(6) + + o.stopEffects() + setA(10) + o.b = 20 + expect(sum1).toBe(6) + expect(sum2).toBe(12) + expect(runs).toBe(6) + }) + + it('runs multiple effects independently using stopEffects', () => { + const [a, setA] = createSignal(1) + let sum1 = 0 + let sum2 = 0 + let runs = 0 + + class Doubler { + @signal b = 3 + @effect eff1() { + runs++ + sum1 = a() + this.b + } + @effect accessor eff2 = () => { + runs++ + sum2 = (a() + this.b) * 2 + } + } + + const o = new Doubler() + expect(sum1).toBe(4) + expect(sum2).toBe(8) + expect(runs).toBe(2) + + setA(2) + expect(sum1).toBe(5) + expect(sum2).toBe(10) + expect(runs).toBe(4) + + o.b = 4 + expect(sum1).toBe(6) + expect(sum2).toBe(12) + expect(runs).toBe(6) + + stopEffects(o) + setA(10) + o.b = 20 + expect(sum1).toBe(6) + expect(sum2).toBe(12) + expect(runs).toBe(6) + }) + + it('reruns effect when memos change inside effect using Effects', () => { + const [a, setA] = createSignal(1) + const [b, setB] = createSignal(2) + let memoVal = 0 + let effectRuns = 0 + + class MemoUser extends Effects { + @memo get sum() { + return a() + b() + } + @effect report() { + effectRuns++ + memoVal = this.sum + } + } + + const m = new MemoUser() + expect(memoVal).toBe(3) + expect(effectRuns).toBe(1) + + setA(5) + expect(memoVal).toBe(7) + expect(effectRuns).toBe(2) + + batch(() => { + setA(6) + setB(1) + }) // sum stays 7 + expect(effectRuns).toBe(2) + + setB(5) + expect(memoVal).toBe(11) + expect(effectRuns).toBe(3) + + m.stopEffects() + setA(0) + setB(0) + expect(memoVal).toBe(11) + expect(effectRuns).toBe(3) + }) + + it('reruns effect when memos change inside effect using stopEffects', () => { + const [a, setA] = createSignal(1) + const [b, setB] = createSignal(2) + let memoVal = 0 + let effectRuns = 0 + + class MemoUser { + @memo get sum() { + return a() + b() + } + @effect report() { + effectRuns++ + memoVal = this.sum + } + } + + const m = new MemoUser() + expect(memoVal).toBe(3) + expect(effectRuns).toBe(1) + + setA(5) + expect(memoVal).toBe(7) + expect(effectRuns).toBe(2) + + batch(() => { + setA(6) + setB(1) + }) // sum stays 7 + expect(effectRuns).toBe(2) + + setB(5) + expect(memoVal).toBe(11) + expect(effectRuns).toBe(3) + + stopEffects(m) + setA(0) + setB(0) + expect(memoVal).toBe(11) + }) + + it('runs an effect on auto accessor using Effects', () => { + const [a, setA] = createSignal(1) + + class AccessorClass extends Effects { + @signal b = 2 + + // Stick this here to ensure that nested constructor doesn't + // interfere with decorator behavior mid-way through initialization + // of the wrapper parent class (tested with a subclass) + child: AccessorClass | null = this.constructor !== AccessorClass ? new AccessorClass() : null + + result = 0 + runs = 0 + @effect accessor compute = () => { + this.runs++ + this.result = a() + this.b + } + } + + class Sub extends AccessorClass {} + + const o = new Sub() + expect(o.result).toBe(3) + expect(o.runs).toBe(1) + + setA(5) + expect(o.result).toBe(7) + expect(o.runs).toBe(2) + o.b = 10 + expect(o.result).toBe(15) + expect(o.runs).toBe(3) + + o.stopEffects() + setA(1) + o.b = 1 + expect(o.result).toBe(15) + expect(o.runs).toBe(3) + }) + + it('runs an effect on auto accessor using stopEffects', () => { + const [a, setA] = createSignal(1) + + class AccessorClass { + @signal b = 2 + + // Stick this here to ensure that nested constructor doesn't + // interfere with decorator behavior mid-way through initialization + // of the wrapper parent class (tested with a subclass) + child: AccessorClass | null = this.constructor !== AccessorClass ? new AccessorClass() : null + + result = 0 + runs = 0 + @effect accessor compute = () => { + this.runs++ + this.result = a() + this.b + } + } + + class Sub extends AccessorClass {} + + const o = new Sub() + expect(o.result).toBe(3) + expect(o.runs).toBe(1) + + setA(5) + expect(o.result).toBe(7) + expect(o.runs).toBe(2) + + o.b = 10 + expect(o.result).toBe(15) + expect(o.runs).toBe(3) + + stopEffects(o) + setA(1) + o.b = 1 + expect(o.result).toBe(15) + expect(o.runs).toBe(3) + }) + + it('managed within an existing root, without Effects, without stopEffects', () => { + const [a, setA] = createSignal(1) + let observed = 0 + let runs = 0 + + class PlainYogurt { + @signal b = 2 + @effect sum() { + runs++ + observed = a() + this.b + } + } + + let p!: PlainYogurt + let dispose!: () => void + + createRoot(d => { + p = new PlainYogurt() + dispose = d + }) + + // As p is created inside a root, it will be tied to that root's owner, + // so this stopEffects(p) will not dispose the effects. + stopEffects(p) + + expect(observed).toBe(3) + expect(runs).toBe(1) + + setA(4) + p.b = 5 + + expect(observed).toBe(9) + expect(runs).toBe(3) + + // Now dispose the root to clean up effects + dispose() + setA(10) + p.b = 20 + expect(observed).toBe(9) // disposed root, no further updates + expect(runs).toBe(3) + }) + + describe('subclass effect overriding/extending', () => { + it('runs subclass effect auto accessor extending base effect auto accessor with super', () => { + const [a, setA] = createSignal(1) + + let baseRuns = 0 + let subRuns = 0 + let observed = 0 + + class Base extends Effects { + @signal b = 2 + @effect accessor eff = () => { + baseRuns++ + observed = a() + this.b + } + } + + class Sub extends Base { + @effect override accessor eff = () => { + subRuns++ + super.eff() + observed = observed + 10 + } + } + + const o = new Sub() + expect(baseRuns).toBe(1) + expect(subRuns).toBe(1) + expect(observed).toBe(1 + 2 + 10) + + o.b = 5 + expect(baseRuns).toBe(2) + expect(subRuns).toBe(2) + expect(observed).toBe(1 + 5 + 10) + + setA(10) + expect(baseRuns).toBe(3) + expect(subRuns).toBe(3) + expect(observed).toBe(10 + 5 + 10) + + o.stopEffects() + o.b = 100 + expect(baseRuns).toBe(3) + expect(subRuns).toBe(3) + expect(observed).toBe(10 + 5 + 10) + }) + + it('runs subclass effect auto accessor overriding base effect auto accessor without super', () => { + const [a, setA] = createSignal(1) + + let baseRuns = 0 + let subRuns = 0 + let observed = 0 + + class Base extends Effects { + @signal b = 2 + @effect accessor eff = () => { + baseRuns++ + observed = a() + this.b + } + } + + class Sub extends Base { + @effect override accessor eff = () => { + subRuns++ + observed = (a() + this.b) * 2 // override without super + } + } + + const o = new Sub() + expect(baseRuns).toBe(0) + expect(subRuns).toBe(1) + expect(observed).toBe((1 + 2) * 2) + + o.b = 5 + expect(baseRuns).toBe(0) + expect(subRuns).toBe(2) + expect(observed).toBe((1 + 5) * 2) + + setA(10) + expect(baseRuns).toBe(0) + expect(subRuns).toBe(3) + expect(observed).toBe((10 + 5) * 2) + + o.stopEffects() + o.b = 100 + expect(baseRuns).toBe(0) + expect(subRuns).toBe(3) + expect(observed).toBe((10 + 5) * 2) + }) + + it('runs subclass effect method extending base effect method with super', () => { + const [a, setA] = createSignal(1) + let superRuns = 0 + let subRuns = 0 + let observed = 0 + + class Base extends Effects { + @signal b = 2 + @effect compute() { + superRuns++ + observed = a() + this.b + } + } + + class Sub extends Base { + @signal c = 3 + @effect override compute() { + subRuns++ + super.compute() + observed += this.c // extend behavior + } + } + + const o = new Sub() + expect(superRuns).toBe(1) + expect(subRuns).toBe(1) + expect(observed).toBe(1 + 2 + 3) // a + b + extension + + setA(5) + expect(superRuns).toBe(2) + expect(subRuns).toBe(2) + expect(observed).toBe(5 + 2 + 3) + + o.b = 10 + expect(superRuns).toBe(3) + expect(subRuns).toBe(3) + expect(observed).toBe(5 + 10 + 3) + + o.c = 5 + expect(superRuns).toBe(4) + expect(subRuns).toBe(4) + expect(observed).toBe(5 + 10 + 5) + + o.stopEffects() + setA(0) + o.b = 0 + o.c = 0 + expect(superRuns).toBe(4) + expect(subRuns).toBe(4) + expect(observed).toBe(5 + 10 + 5) + }) + + it('supports multi-level effect method extending base effect method with super', () => { + const [a, setA] = createSignal(1) + let baseRuns = 0 + let midRuns = 0 + let subRuns = 0 + let observed = 0 + + class Base extends Effects { + @signal b = 2 + @effect compute() { + baseRuns++ + observed = a() + this.b + } + } + + class Mid extends Base { + @signal c = 3 + @effect override compute() { + midRuns++ + super.compute() + observed += this.c + } + } + + class Sub extends Mid { + @signal d = 4 + @effect override compute() { + subRuns++ + super.compute() + observed += this.d + } + } + + const o = new Sub() + expect(baseRuns).toBe(1) + expect(midRuns).toBe(1) + expect(subRuns).toBe(1) + expect(observed).toBe(1 + 2 + 3 + 4) + + setA(5) + expect(baseRuns).toBe(2) + expect(midRuns).toBe(2) + expect(subRuns).toBe(2) + expect(observed).toBe(5 + 2 + 3 + 4) + + o.b = 10 + expect(baseRuns).toBe(3) + expect(midRuns).toBe(3) + expect(subRuns).toBe(3) + expect(observed).toBe(5 + 10 + 3 + 4) + + o.c = 6 + expect(baseRuns).toBe(4) + expect(midRuns).toBe(4) + expect(subRuns).toBe(4) + expect(observed).toBe(5 + 10 + 6 + 4) + + o.d = 7 + expect(baseRuns).toBe(5) + expect(midRuns).toBe(5) + expect(subRuns).toBe(5) + expect(observed).toBe(5 + 10 + 6 + 7) + + o.stopEffects() + setA(0) + o.b = 0 + o.c = 0 + o.d = 0 + expect(baseRuns).toBe(5) + expect(midRuns).toBe(5) + expect(subRuns).toBe(5) + expect(observed).toBe(5 + 10 + 6 + 7) + }) + + it('runs subclass effect method overriding base effect method without super', () => { + const [a, setA] = createSignal(1) + let superRuns = 0 + let subRuns = 0 + let observed = 0 + + class Base extends Effects { + @signal b = 2 + @effect compute() { + superRuns++ + observed = a() + this.b + } + } + + class Sub extends Base { + @effect override compute() { + subRuns++ + observed = (a() + this.b) * 2 // override without super + } + } + + const o = new Sub() + expect(superRuns).toBe(0) + expect(subRuns).toBe(1) + expect(observed).toBe((1 + 2) * 2) + + setA(3) + expect(superRuns).toBe(0) + expect(subRuns).toBe(2) + expect(observed).toBe((3 + 2) * 2) + + o.b = 5 + expect(superRuns).toBe(0) + expect(subRuns).toBe(3) + expect(observed).toBe((3 + 5) * 2) + + o.stopEffects() + setA(10) + o.b = 1 + expect(subRuns).toBe(3) + }) + }) + + it('works with nested effects', () => { + let outerRuns = 0 + let innerRuns = 0 + + class MyEffects { + @signal a = 0 + @signal b = 0 + + @effect outer() { + outerRuns++ + this.a + + createEffect(() => { + innerRuns++ + this.b + }) + } + } + + const e = new MyEffects() + + expect(outerRuns).toBe(1) + expect(innerRuns).toBe(1) + + startEffects(e) // should not duplicate effects (already started) + + expect(outerRuns).toBe(1) + expect(innerRuns).toBe(1) + + e.a = 1 + expect(outerRuns).toBe(2) + expect(innerRuns).toBe(2) // inner effect runs because outer effect re-ran + + e.b = 1 + expect(outerRuns).toBe(2) + expect(innerRuns).toBe(3) // inner effect runs independently + + stopEffects(e) + + expect(outerRuns).toBe(2) + expect(innerRuns).toBe(3) + + startEffects(e) + + expect(outerRuns).toBe(3) + expect(innerRuns).toBe(4) // inner effect runs because outer effect re-ran + + e.b = 2 + expect(outerRuns).toBe(3) + expect(innerRuns).toBe(5) // inner effect runs independently + }) + + describe('invalid usage', () => { + it('throws on invalid field usage', () => { + expect(() => { + class BadField { + // @ts-expect-error invalid decorator usage on field + @effect nope = () => 123 + } + new BadField() + }).toThrow('@effect can only be used on methods or function-valued accessors') + }) + + it('throws on invalid getter usage', () => { + expect(() => { + class BadGetter { + @signal a = 1 + // @ts-expect-error invalid decorator usage on getter + @effect get nope() { + return this.a + } + } + new BadGetter() + }).toThrow('@effect can only be used on methods or function-valued accessors') + }) + + it('throws on invalid static usage', () => { + expect(() => { + class BadStatic { + @effect static nope() {} + } + BadStatic + }).toThrow('@effect is not supported on static members.') + }) + + it('throws on invalid non-function value', () => { + expect(() => { + class NonFunction { + @signal a = 1 + // @ts-expect-error invalid decorator usage on non-function + @effect accessor nope: number = 123 + } + new NonFunction() + }).toThrow('@effect decorated member "nope" is not a function') + }) + + it('throws on duplicate members', () => { + const run = () => { + class SuperDuper { + // @ts-expect-error duplicate member + @effect dupe() { + this + } + + // @ts-expect-error duplicate member + @effect dupe() { + this + } + } + + new SuperDuper() + } + + // When compiling with Babel, decorators currently throw an error when applied onto multiple members of the same name. + expect(run).toThrow('Decorating two elements with the same name (dupe) is not supported yet') + + // When compiling with TypeScript, decorating duplicate members is allowed, and the last one wins. + // expect(run).toThrow( + // '@effect decorated member "dupe" has already been effectified. This can happen if there are duplicated class members.', + // ) + }) + }) + + describe('usage with custom elements', () => { + it('@effect methods, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => { + const el = document.createElement('my-element4') as MyElement4 + expect(el.result).toBe(1 + 2) + expect(el.runs).toBe(1) // already ran in constructor + + testElementEffects(el) + }) + }) + }) +}) diff --git a/src/decorators/effect.ts b/src/decorators/effect.ts new file mode 100644 index 0000000..3370a40 --- /dev/null +++ b/src/decorators/effect.ts @@ -0,0 +1,134 @@ +import {effectifyIfNeeded, finalizeMembersIfLast, getMemberStat, getMembers, effects__} from '../_state.js' +import type {AnyObject, ClassySolidMetadata} from './types.js' +import './metadata-shim.js' + +/** + * Decorator for making Solid.js effects out of methods or function-valued + * properties. This is more convenient than calling `this.createEffect()` in the + * constructor or a class method, reducing boilerplate. Pair this with `@signal` + * and `@memo` to make reactive classes with less code. + * + * The `@effect` decorator can be used on methods or auto accessors. Methods are + * the recommended usage. + * + * When used on auto accessors, the auto accessor value must be a function. Note + * that currently the auto accessor function value cannot be changed (if you + * change it, the new function will not be used). + * + * Example: + * + * ```ts + * import { effect, signal, stopEffects } from 'classy-solid' + * import { createSignal } from 'solid-js' + * + * const [a, setA] = createSignal(1) + * + * class Funkalicious { + * @signal b = 2 + * + * @effect logSum() { + * console.log('Sum:', a() + this.b) + * } + * + * // Not recommended, but supported (more concise for simple effects): + * @effect accessor logA = () => console.log('a:', a()) + * } + * + * const fun = new Funkalicious() // logs "Sum: 3" + * + * setA(5) // logs "Sum: 7", "a: 5" + * fun.b = 10 // logs "Sum: 15" + * + * // Later, clean up when done... + * stopEffects(fun) + * ``` + * + * When extending from Effectful() or Effects, the `stopEffects` method can + * be used instead of the standalone `stopEffects()` function: + * + * ```ts + * import { effect, signal, Effects } from 'classy-solid' + * import { createSignal } from 'solid-js' + * + * const [a, setA] = createSignal(1) + * + * class Funkalicious extends Effects { + * @signal b = 2 + * + * @effect logSum() { + * console.log('Sum:', a() + this.b) + * } + * } + * + * const fun = new Funkalicious() // logs "Sum: 3" + * + * setA(5) // logs "Sum: 7" + * fun.b = 10 // logs "Sum: 15" + * + * // Later, clean up when done... + * fun.stopEffects() + * ``` + */ + +export function effect( + value: Function | ClassAccessorDecoratorTarget void>, + context: ClassMethodDecoratorContext | ClassAccessorDecoratorContext, +) { + if (context.static) throw new Error('@effect is not supported on static members.') + + const {kind, name} = context + const metadata = context.metadata as ClassySolidMetadata + const signalsAndMemos = getMembers(metadata) + + if (!(kind === 'method' || kind === 'accessor')) + throw new Error('@effect can only be used on methods or function-valued accessors') + + const stat = + kind === 'accessor' + ? getMemberStat(name, 'effect-auto-accessor', signalsAndMemos) + : getMemberStat(name, 'effect-method', signalsAndMemos) + + stat.finalize = function (this: unknown) { + effectifyIfNeeded(this as AnyObject, name, stat) + } + + context.addInitializer(function () { + finalizeMembersIfLast(this as AnyObject, signalsAndMemos) + }) + + if (kind === 'method') stat.value = value + else if (kind === 'accessor') stat.value = (value as ClassAccessorDecoratorTarget void>).get +} + +/** + * Starts all Solid.js effects created by the `@effect` decorator on the given + * object. This can be used to restart effects that were previously stopped with + * `stopEffects()`. + * + * Effects are created and started automatically, so this only needs to be + * called if you have previously stopped the effects and want to start them + * again. + */ +export function startEffects(obj: object) { + let effects = effects__.get(obj as AnyObject) + if (!effects) return + effects.startEffects() +} + +/** + * Stops all Solid.js effects created by the `@effect` decorator on the given + * object. Use this once you are done with the instance and need to clean up. + * + * This does not needed to be used if the object is created in a reactive + * context (such as inside a Solid.js component, or a nested effect), as those + * effects will be cleaned up automatically when the owner context is cleaned + * up. + * + * Effects that have been stopped can later be restarted by calling + * `startEffects(obj)`. + */ +export function stopEffects(obj: object) { + const effects = effects__.get(obj as AnyObject) + if (!effects) return + effects.stopEffects() +} diff --git a/src/decorators/index.ts b/src/decorators/index.ts index 609be83..f030cfa 100644 --- a/src/decorators/index.ts +++ b/src/decorators/index.ts @@ -1,4 +1,5 @@ export * from './component.js' +export * from './effect.js' export * from './memo.js' export * from './reactive.js' export * from './signal.js' diff --git a/src/decorators/memo.test.ts b/src/decorators/memo.test.ts index e72cf50..49455a3 100644 --- a/src/decorators/memo.test.ts +++ b/src/decorators/memo.test.ts @@ -1,90 +1,10 @@ -import {createEffect, batch} from 'solid-js' +import {createEffect, batch, createSignal} from 'solid-js' import {signal} from './signal.js' import {memo} from './memo.js' +import {effect} from './effect.js' describe('classy-solid', () => { - describe('@memo', () => { - it('creates a readonly memo via field', () => { - class Example { - @signal a = 1 - @signal b = 2 - - @memo sum = () => this.a + this.b - } - - const ex = new Example() - let count = 0 - let lastSum = 0 - - createEffect(() => { - lastSum = ex.sum() - count++ - }) - - expect(ex.sum()).toBe(3) - expect(count).toBe(1) - - ex.a = 5 - expect(ex.sum()).toBe(7) - expect(lastSum).toBe(7) - expect(count).toBe(2) - - // This should not trigger the effect since the computed value doesn't change (still 7) - batch(() => { - ex.a = 3 - ex.b = 4 - }) - - expect(ex.sum()).toBe(7) - expect(lastSum).toBe(7) - expect(count).toBe(2) // count should still be 2, not 3 - - // @ts-expect-error Readonly memo cannot be set - should throw - expect(() => ex.sum(20)).toThrow() - }) - - it('creates a writable memo via field', () => { - class Example { - @signal a = 1 - @signal b = 2 - - @memo sum = (_val?: number) => this.a + this.b - } - - const ex = new Example() - let count = 0 - let lastSum = 0 - - createEffect(() => { - lastSum = ex.sum() - count++ - }) - - expect(ex.sum()).toBe(3) - expect(count).toBe(1) - - ex.a = 5 - expect(ex.sum()).toBe(7) - expect(lastSum).toBe(7) - expect(count).toBe(2) - - // This should not trigger the effect since the computed value doesn't change (still 7) - batch(() => { - ex.a = 3 - ex.b = 4 - }) - - expect(ex.sum()).toBe(7) - expect(lastSum).toBe(7) - expect(count).toBe(2) // count should still be 2, not 3 - - // Writable memo can be set directly - ex.sum(20) - expect(ex.sum()).toBe(20) - expect(lastSum).toBe(20) - expect(count).toBe(3) - }) - + describe('@memo decorator', () => { it('creates a readonly memo via getter', () => { class Example { @signal a = 1 @@ -183,21 +103,21 @@ describe('classy-solid', () => { } const ex = new Example() - let count = 0 + let runs = 0 let lastSum = 0 createEffect(() => { lastSum = ex.sum3() - count++ + runs++ }) expect(ex.sum3()).toBe(3) - expect(count).toBe(1) + expect(runs).toBe(1) ex.a = 5 expect(ex.sum3()).toBe(7) expect(lastSum).toBe(7) - expect(count).toBe(2) + expect(runs).toBe(2) // This should not trigger the effect since the computed value doesn't change (still 7) batch(() => { @@ -207,7 +127,7 @@ describe('classy-solid', () => { expect(ex.sum3()).toBe(7) expect(lastSum).toBe(7) - expect(count).toBe(2) // count should still be 2, not 3 + expect(runs).toBe(2) // count should still be 2, not 3 // @ts-expect-error Readonly memo cannot be set - should throw expect(() => ex.sum3(20)).toThrow() @@ -378,7 +298,9 @@ describe('classy-solid', () => { class MultiMemo { @signal value = 10 - @memo double = () => this.value * 2 + @memo double() { + return this.value * 2 + } @memo get triple() { return this.value * 3 } @@ -450,41 +372,6 @@ describe('classy-solid', () => { expect(count).toBe(2) }) - it('correctly handles writable field memo overriding explicit value', () => { - class WritableOverride { - @signal a = 5 - @signal b = 10 - - @memo sum = (_val?: number) => this.a + this.b - } - - const wo = new WritableOverride() - let count = 0 - let lastValue = 0 - - createEffect(() => { - lastValue = wo.sum() - count++ - }) - - expect(wo.sum()).toBe(15) - expect(count).toBe(1) - expect(lastValue).toBe(15) - - // Override with direct value - wo.sum(100) - expect(wo.sum()).toBe(100) - expect(count).toBe(2) - expect(lastValue).toBe(100) - - // Changing dependencies should still work after override - wo.a = 20 - // The memo should now compute based on signals again - expect(wo.sum()).toBe(30) // 20 + 10 - expect(count).toBe(3) - expect(lastValue).toBe(30) - }) - it('correctly handles writable getter+setter memo overriding explicit value', () => { class WritableOverride { @signal a = 5 @@ -618,5 +505,416 @@ describe('classy-solid', () => { expect(val).toBe(42) expect(count).toBe(1) }) + + describe('subclass memo overriding/extending', () => { + it('supports subclass memo extending base memo (getter)', () => { + class Base { + @signal a = 1 + @memo get baseVal() { + return this.a + 1 + } + } + + class Sub extends Base { + @memo override get baseVal() { + return super.baseVal + 1 // extend + } + } + + const s = new Sub() + let runs = 0 + let last = 0 + + createEffect(() => { + runs++ + last = s.baseVal + }) + + expect(last).toBe(1 + 1 + 1) + expect(runs).toBe(1) + + s.a = 5 + expect(last).toBe(5 + 1 + 1) + expect(runs).toBe(2) + }) + + it('supports subclass memo overriding base memo (getter no super)', () => { + class Base { + @signal a = 1 + @memo get val() { + return this.a + 1 + } + } + + class Sub extends Base { + @memo override get val() { + return this.a * 2 // override + } + } + + const s = new Sub() + let runs = 0 + let last = 0 + + createEffect(() => { + runs++ + last = s.val + }) + + expect(last).toBe(1 * 2) + expect(runs).toBe(1) + + s.a = 5 + expect(last).toBe(5 * 2) + expect(runs).toBe(2) + }) + + it('supports getter override with no super', () => { + const [a, setA] = createSignal(10) + let baseRuns = 0 + let subRuns = 0 + + class Base { + @memo get val() { + baseRuns++ + return a() + 1 + } + } + + class Sub extends Base { + @memo override get val() { + subRuns++ + return a() + 10 + } + } + + const o = new Sub() + let effectRuns = 0 + let effectVal = 0 + + createEffect(() => { + effectRuns++ + effectVal = o.val + }) + + expect(effectVal).toBe(10 + 10) + expect(baseRuns).toBe(0) + expect(subRuns).toBe(1) + expect(effectRuns).toBe(1) + + setA(20) + expect(effectVal).toBe(20 + 10) + expect(baseRuns).toBe(0) + expect(subRuns).toBe(2) + expect(effectRuns).toBe(2) + }) + + it('supports multi-level getter extension with super', () => { + const [a, setA] = createSignal(10) + let baseRuns = 0 + let midRuns = 0 + let subRuns = 0 + + class Base { + @memo get val() { + baseRuns++ + return a() + 1 + } + } + + class Mid extends Base { + @memo override get val() { + midRuns++ + return super.val + 10 + } + } + + class Sub extends Mid { + @memo override get val() { + subRuns++ + return super.val + 100 + } + } + + const o = new Sub() + let effectRuns = 0 + let effectVal = 0 + + createEffect(() => { + effectRuns++ + effectVal = o.val + }) + + expect(effectVal).toBe(10 + 1 + 10 + 100) + expect(baseRuns).toBe(1) + expect(midRuns).toBe(1) + expect(subRuns).toBe(1) + expect(effectRuns).toBe(1) + + setA(20) + expect(effectVal).toBe(20 + 1 + 10 + 100) + expect(baseRuns).toBe(2) + expect(midRuns).toBe(2) + expect(subRuns).toBe(2) + expect(effectRuns).toBe(2) + }) + + it('supports subclass memo method extension with super', () => { + let baseRuns = 0 + + class BaseM { + @signal a = 1 + @memo val() { + baseRuns++ + return this.a + 1 + } + } + + let subRuns = 0 + + class SubM extends BaseM { + @memo override val() { + subRuns++ + return super.val() + 2 + } + } + + const s = new SubM() + let effectRuns = 0 + let last = 0 + + createEffect(() => { + effectRuns++ + last = s.val() + }) + + expect(last).toBe(1 + 1 + 2) + expect(baseRuns).toBe(1) + expect(subRuns).toBe(1) + expect(effectRuns).toBe(1) + + s.a = 5 + expect(last).toBe(5 + 1 + 2) + expect(baseRuns).toBe(2) + expect(subRuns).toBe(2) + expect(effectRuns).toBe(2) + }) + + it('supports subclass memo method override with no super', () => { + let baseRuns = 0 + let subRuns = 0 + + class BaseM { + @signal a = 1 + @memo val() { + baseRuns++ + return this.a + 1 + } + } + + class SubM extends BaseM { + @memo override val() { + subRuns++ + return this.a + 2 + } + } + + const s = new SubM() + let effectRuns = 0 + let last = 0 + + createEffect(() => { + effectRuns++ + last = s.val() + }) + + expect(last).toBe(1 + 2) + expect(baseRuns).toBe(0) + expect(subRuns).toBe(1) + expect(effectRuns).toBe(1) + + s.a = 5 + expect(last).toBe(5 + 2) + expect(baseRuns).toBe(0) + expect(subRuns).toBe(2) + expect(effectRuns).toBe(2) + }) + + it('supports subclass memo auto accessor extension with super', () => { + let baseRuns = 0 + let subRuns = 0 + + class BaseFO { + @signal a = 1 + @memo accessor val = () => { + baseRuns++ + return this.a + 1 + } + } + + class SubFO extends BaseFO { + @memo override accessor val = () => { + subRuns++ + return super.val() * 3 + } + } + + const s = new SubFO() + let effectRuns = 0 + let last = 0 + + createEffect(() => { + effectRuns++ + last = s.val() + }) + + expect(last).toBe((1 + 1) * 3) + expect(baseRuns).toBe(1) + expect(subRuns).toBe(1) + expect(effectRuns).toBe(1) + + s.a = 4 + expect(last).toBe((4 + 1) * 3) + expect(baseRuns).toBe(2) + expect(subRuns).toBe(2) + expect(effectRuns).toBe(2) + }) + + it('supports subclass memo auto accessor override with no super', () => { + let baseRuns = 0 + let subRuns = 0 + + class BaseFO { + @signal a = 1 + @memo accessor val = () => { + baseRuns++ + return this.a + 1 + } + } + + class SubFO extends BaseFO { + @memo override accessor val = () => { + subRuns++ + return this.a * 3 + } + } + + const s = new SubFO() + let effectRuns = 0 + let last = 0 + + createEffect(() => { + effectRuns++ + last = s.val() + }) + + expect(last).toBe(1 * 3) + expect(baseRuns).toBe(0) + expect(subRuns).toBe(1) + expect(effectRuns).toBe(1) + + s.a = 4 + expect(last).toBe(4 * 3) + expect(baseRuns).toBe(0) + expect(subRuns).toBe(2) + expect(effectRuns).toBe(2) + }) + }) + + describe('invalid usage', () => { + it('throws on non-function value', () => { + class Base { + // @ts-expect-error non-function value + @memo accessor foo = 1 + } + + expect(() => new Base()).toThrow('memo value for "foo" is not a function: 1') + }) + + it('throws on @memo used on class field', () => { + const [a] = createSignal(10) + + expect(() => { + class InvalidMemo { + // @ts-expect-error @memo not usable on fields + @memo a = () => a() + } + new InvalidMemo() + }).toThrow('@memo is not supported on class fields.') + }) + + it('throws on duplicate members', () => { + const run = () => { + class SuperDuper { + // @ts-expect-error duplicate member + @memo get dupe() { + return 2 + } + // @ts-expect-error duplicate member + @memo get dupe() { + return 3 + } + } + + new SuperDuper() + } + + // When compiling with Babel, decorators currently throw an error when applied onto multiple members of the same name. + expect(run).toThrow('Decorating two elements with the same name (get dupe) is not supported yet') + + // When compiling with TypeScript, decorating duplicate members is allowed, and the last one wins. + // expect(run).toThrow( + // '@memo decorated member "dupe" has already been memoified. This can happen if there are duplicated class members.', + // ) + + // TODO ^ update Babel to latest in @lume/cli, see if decorators on duplicate members work in classy-solid + }) + + it('throws due to TDZ when accessing private fields defined after regular fields', () => { + class Bar { + @signal bar = 456 + + #baz = 789 + + @signal get baz() { + return this.#baz + } + @signal set baz(v) { + this.#baz = v + } + + // This throws because #baz is used before its initialization + // The ordering is: + // 1. bar field initialized + // 2. bar field runs finalizers because it is last in the ordering of extra initializers (so #baz is not initialized yet) + // 3. During the logBar finalizer (executed in the bar extra initializer), the baz getter is accessed, which accesses #baz before it is initialized + @effect logBar() { + console.log('this.baz:', this.baz) + } + } + + expect(() => new Bar()).toThrow('Cannot read private member #baz from an object whose class did not declare it') + + // To work around the problem, place private fields before regular fields: + class Bar2 { + #baz = 789 + + @signal bar = 456 + + @signal get baz() { + return this.#baz + } + @signal set baz(v) { + this.#baz = v + } + + @effect logBar() { + console.log('this.baz:', this.baz) + } + } + + expect(() => new Bar2()).not.toThrow() + }) + }) }) }) diff --git a/src/decorators/memo.ts b/src/decorators/memo.ts index 08a9d64..17e0328 100644 --- a/src/decorators/memo.ts +++ b/src/decorators/memo.ts @@ -1,12 +1,6 @@ -import type {SignalMetadata} from './types.js' -import { - finalizeMemos, - getMemberStat, - getSignalsAndMemos, - isPriorSignalOrMemoDefined, - memoifyIfNeeded, - sortSignalsMemosInMetadata, -} from '../_state.js' +import type {AnyObject, ClassySolidMetadata} from './types.js' +import {finalizeMembersIfLast, getMemberStat, getMembers, memoifyIfNeeded} from '../_state.js' +import './metadata-shim.js' /** * A decorator that make a signal property derived from a memoized computation @@ -18,36 +12,43 @@ import { * ```ts * import {reactive, signal, memo} from "classy-solid"; * - * @reactive * class Example { * @signal a = 1 * @signal b = 2 * - * // @memo can be used on a field, getter, or accessor. - * - * // Writable memo via field (requires function to have a parameter (arity > 0)) - * @memo sum = (v?: number) => this.a + this.b + * // @memo can be used on a getter, accessor, or method, with readonly and + * // writable variants: * * // Readonly memo via getter only + * @memo get sum1() { + * return this.a + this.b + * } + * + * // Writable memo via getter with setter * @memo get sum2() { * return this.a + this.b * } + * @memo set sum2(value: number) { + * // use an empty setter to enable writable (logic in here will be ignored if any) + * } * - * // Readonly memo via accessor (requires arrow function, not writable because no parameter (arity = 0)) + * // Readonly memo via auto accessor (requires arrow function, not writable because no parameter (arity = 0)) * @memo accessor sum3 = () => this.a + this.b * + * // Writable memo via auto accessor (requires arrow function with parameter, arity > 0) + * // Ignore the parameter, it only enables writable memo + * @memo accessor sum4 = (v?: number) => this.a + this.b + * * // Readonly memo via method - * @memo sum4() { + * @memo sum5() { * return this.a + this.b * } * - * // Writable memo via getter with setter - * @memo get sum5() { + * // Writable memo via method with parameter (arity > 0) + * @memo sum6(value?: number) { + * // Ignore the parameter, it only enables writable memo * return this.a + this.b * } - * @memo set sum5(value: number) { - * // empty setter makes it writable (logic in here will be ignored if any) - * } * * // The following variants are not supported yet as no runtime or TS support exists yet for the syntax. * @@ -75,32 +76,31 @@ import { * } * } * - * const ex = new Example(); + * const ex = new Example() * - * console.log(ex.sum(), ex.sum2, ex.sum3(), ex.sum4(), ex.sum5); // 3 3 3 3 3 + * console.log(ex.sum1, ex.sum2, ex.sum3(), ex.sum4(), ex.sum5(), ex.sum6()) // 3 3 3 3 3 3 * * createEffect(() => { - * console.log(ex.sum(), ex.sum2, ex.sum3(), ex.sum4(), ex.sum5); + * console.log(ex.sum1, ex.sum2, ex.sum3(), ex.sum4(), ex.sum5(), ex.sum6()) * }); * - * ex.a = 5; // Logs: 7 7 7 7 7 + * ex.a = 5 // Logs: 7 7 7 7 7 7 * * // This won't log anything since the computed memo values don't change (all still 7). * batch(() => { - * ex.a = 3; - * ex.b = 4; + * ex.a = 3 + * ex.b = 4 * }) * - * ex.sum(20); // Logs: 20 7 7 7 7 (only sum is updated) + * ex.sum2 = 20 // Logs: 20 7 7 7 7 7 (only sum2 is updated) * - * ex.sum5 = 15; // Logs: 20 7 7 7 15 (only sum5 is updated) + * ex.sum6(15) // Logs: 20 7 7 7 7 15 (only sum6 is updated) * - * ex.sum2 = 10; // Runtime error: Cannot set readonly property "sum2". + * ex.sum1 = 10 // Runtime error: Cannot set readonly property "sum1". * ``` */ export function memo( - _value: - | undefined + value: | ((val?: any) => any) // writable memo via field or method | (() => any) // readonly memo via field or method | ((val?: any) => void) // memo setter @@ -108,7 +108,6 @@ export function memo( | ClassAccessorDecoratorTarget any> // today's auto-accessors, readonly memo | ClassAccessorDecoratorTarget any>, // today's auto-accessors, writable memo context: - | ClassFieldDecoratorContext | ClassGetterDecoratorContext | ClassSetterDecoratorContext | ClassAccessorDecoratorContext @@ -117,60 +116,30 @@ export function memo( if (context.static) throw new Error('@memo is not supported on static fields yet.') const {kind, name} = context - const metadata = context.metadata as SignalMetadata - const signalsAndMemos = getSignalsAndMemos(metadata) - - if (kind === 'field') { - const stat = getMemberStat(name, 'memo-field', signalsAndMemos) - - context.addInitializer(function () { - sortSignalsMemosInMetadata(metadata) - memoifyIfNeeded(this as object, name, stat) - - // If we skipped memoifying prior memo members (accessor and method - // memos) because of prior signal-fields, memo-fields, or - // memo-auto-accessors, finalize those memos now. - finalizeMemos(this as object, stat, signalsAndMemos) - }) - } else if (kind === 'accessor') { - const stat = getMemberStat(name, 'memo-auto-accessor', signalsAndMemos) + const metadata = context.metadata as ClassySolidMetadata + const signalsAndMemos = getMembers(metadata) - context.addInitializer(function () { - sortSignalsMemosInMetadata(metadata) - memoifyIfNeeded(this as object, name, stat, true) + // Apply finalization logic to all except setters (setters are finalized + // together with their getters). + // By skipping setters we also avoid double-counting the getter+setter pair + // in the finalizeMembersIfLast logic. + if (kind === 'setter') return - // If we skipped memoifying prior memo members (accessor and method - // memos) because of prior signal-fields, memo-fields, or - // memo-auto-accessors, finalize those memos now. - finalizeMemos(this as object, stat, signalsAndMemos) - }) - } else if (kind === 'method') { - const stat = getMemberStat(name, 'memo-method', signalsAndMemos) + // @ts-expect-error skip type checking in case of invalid kind in plain JS + if (kind === 'field') throw new Error('@memo is not supported on class fields.') - context.addInitializer(function () { - sortSignalsMemosInMetadata(metadata) + const memberType = kind === 'accessor' ? 'memo-auto-accessor' : kind === 'method' ? 'memo-method' : 'memo-accessor' - // If any signal-fields, memo-fields, or memo-auto-accessors are - // defined on the class (thus sorted before this memo method), skip - // memoifying now because we need those to be initialized first, - // then we'll memoify after that. - if (isPriorSignalOrMemoDefined(this as object, name, signalsAndMemos)) return + const stat = getMemberStat(name, memberType, signalsAndMemos) - memoifyIfNeeded(this as object, name, stat) - }) - } else if (kind === 'getter' || kind === 'setter') { - const stat = getMemberStat(name, 'memo-accessor', signalsAndMemos) - - context.addInitializer(function () { - sortSignalsMemosInMetadata(metadata) + stat.finalize = function (this: unknown) { + memoifyIfNeeded(this as AnyObject, name, stat) + } - // If any signal-fields, memo-fields, or memo-auto-accessors are - // defined on the class (thus sorted before this memo method), skip - // memoifying now because we need those to be initialized first, - // then we'll memoify after that. - if (isPriorSignalOrMemoDefined(this as object, name, signalsAndMemos)) return + context.addInitializer(function () { + finalizeMembersIfLast(this as AnyObject, signalsAndMemos) + }) - memoifyIfNeeded(this as object, name, stat) - }) - } + if (kind === 'method' || kind === 'getter') stat.value = value + else if (kind === 'accessor') stat.value = (value as ClassAccessorDecoratorTarget void>).get } diff --git a/src/decorators/metadata-shim.ts b/src/decorators/metadata-shim.ts new file mode 100644 index 0000000..8c9b8e6 --- /dev/null +++ b/src/decorators/metadata-shim.ts @@ -0,0 +1,13 @@ +// Until decorators land natively, we need this shim so that we can use +// decorator metadata. https://github.com/microsoft/TypeScript/issues/53461 + +export {} // we don't export anything, but this denotes the file as a module to TypeScript + +declare global { + interface SymbolConstructor { + readonly metadata: unique symbol + } +} + +// @ts-expect-error readonly +Symbol.metadata ??= Symbol.for('Symbol.metadata') diff --git a/src/decorators/signal.test.ts b/src/decorators/signal.test.ts index 50583e2..9b6a53b 100644 --- a/src/decorators/signal.test.ts +++ b/src/decorators/signal.test.ts @@ -1,15 +1,24 @@ -import {createEffect} from 'solid-js' +import {$PROXY, createEffect} from 'solid-js' +import {createMutable} from 'solid-js/store' import {testButterflyProps} from '../index.test.js' import {signal} from './signal.js' import {signalify} from '../signals/signalify.js' +import type {ClassySolidMetadata} from './types.js' +import {isSignalGetter} from '../_state.js' +import {memo} from './memo.js' describe('classy-solid', () => { - describe('@signal', () => { + describe('@signal decorator', () => { class Butterfly { @signal colors = 3 #wingSize = 2 + // Stick this here to ensure that nested constructor doesn't + // interfere with decorator behavior mid-way through initialization + // of the wrapper parent class (tested with a subclass) + child: Butterfly | null = this.constructor !== Butterfly ? new Butterfly() : null + @signal get wingSize() { return this.#wingSize } @@ -165,14 +174,13 @@ describe('classy-solid', () => { expect(count).toBe(2) } - const ensure = it - - ensure('overridden fields work as expected', async () => { + it('allows overridden fields to work as expected', async () => { class Mid extends Butterfly { override colors = 0 } // ensure subclass did not interfere with functionality of base class + new Butterfly() // ensure first instantiation doesn't affect later ones const b0 = new Butterfly() testProp(b0, 'colors', 3, 4, true) expect(Object.getOwnPropertyDescriptor(b0, 'colors')?.get?.call(b0) === 4).toBe(true) // accessor descriptor @@ -304,5 +312,276 @@ describe('classy-solid', () => { expect(count).toBe(2) // it would be 3 if there were an extra signal } }) + + it('throws on invalid usage', () => { + expect(() => { + class InvalidStatic { + @signal static val = 1 + } + new InvalidStatic() + }).toThrowError('@signal is not supported on static fields yet.') + + expect(() => { + class InvalidMethod { + // @ts-expect-error type error because method is invalid + @signal method() { + return 1 + } + } + new InvalidMethod() + }).toThrowError('The @signal decorator is only for use on fields, getters, setters, and auto accessors.') + }) + + it('no-ops with Solid proxies to avoid an unnecessary extra signal', () => { + let plain!: Human + let proxy!: Human + + class Human { + constructor() { + plain = this + return (proxy = createMutable(this)) + } + } + + let metadata!: ClassySolidMetadata + + const _signal: typeof signal = (_, context) => { + metadata = context.metadata as ClassySolidMetadata + return signal(_, context) + } + + let memoRuns = 0 + + class CoolKid extends Human { + @_signal age = 3 + + @memo get ageInDogYears() { + memoRuns++ + return this.age * 7 + } + } + + const kid = new CoolKid() + + // Verify we got a Solid Proxy. + expect(plain === proxy).toBe(false) + expect((plain as any)[$PROXY] === proxy).toBe(true) + + expect(metadata.classySolid_members!.find(m => m.name === 'age')!.applied.get(kid)).toBe(true) + + // Verify it there is not our own signal getter applied (it may be + // the Solid Proxy's, or none, depending on how the Solid Proxy + // implementation goes). + const descriptor = Object.getOwnPropertyDescriptor(kid, 'age') + const getter = descriptor!.get! + expect(isSignalGetter.has(getter)).toBe(false) + + let count = 0 + createEffect(() => { + count++ + kid.age + }) + + expect(count).toBe(1) + expect(kid.age).toBe(3) + // check that @memo still works with the Proxy + expect(memoRuns).toBe(1) + expect(kid.ageInDogYears).toBe(21) + + kid.age = 4 + + expect(count).toBe(2) + expect(kid.age).toBe(4) + // check that @memo still works with the Proxy + expect(memoRuns).toBe(2) + expect(kid.ageInDogYears).toBe(28) + }) + + describe('subclass signal overriding/extending', () => { + it('supports subclass signal field extending base signal field', () => { + class Base { + @signal val = 1 + } + + class Sub extends Base { + // @ts-ignore this is valid in plain JS, TS complains about using field before initialization + @signal override val = this.val + 1 // override field with initial value from base class + } + + const s = new Sub() + let count = 0 + createEffect(() => { + count++ + s.val + }) + + expect(s.val).toBe(2) + expect(count).toBe(1) + + s.val = 5 + expect(s.val).toBe(5) + expect(count).toBe(2) + }) + + it('supports subclass signal auto accessor extending base signal auto accessor with super', () => { + class Base { + @signal accessor n = 1 + } + + class Sub extends Base { + @signal override accessor n = super.n + 1 // initialize with initial super value + } + + const s = new Sub() + let count = 0 + createEffect(() => { + count++ + s.n + }) + + expect(s.n).toBe(2) + expect(count).toBe(1) + + s.n = 7 + expect(s.n).toBe(7) + expect(count).toBe(2) + }) + + it('supports subclass signal getter/setter extending base signal getter/setter with super', () => { + class Base { + #n = 1 + @signal get n() { + return this.#n + } + @signal set n(v: number) { + this.#n = v + } + } + + class Sub extends Base { + @signal override get n() { + return super.n + 1 // extend read + } + @signal override set n(v: number) { + super.n = v + 1 // extend write + } + } + + const s = new Sub() + let count = 0 + let last = 0 + createEffect(() => { + count++ + last = s.n + }) + + expect(last).toBe(1 + 1) + expect(count).toBe(1) + + s.n = 10 + expect(last).toBe(10 + 1 + 1) + expect(count).toBe(2) + }) + + it('supports multi-level signal getter/setter extension with super', () => { + let runs = 0 + class Base { + _val = 1 + @signal get val() { + return this._val + } + @signal set val(v) { + this._val = v + } + } + class Mid extends Base { + @signal override get val() { + return super.val + 10 + } + @signal override set val(v) { + super.val = v - 10 + } + } + class Sub extends Mid { + @signal override get val() { + return super.val + 100 + } + @signal override set val(v) { + super.val = v - 100 + } + } + const o = new Sub() + + createEffect(() => { + runs++ + o.val + }) + + expect(o._val).toBe(1) + expect(o.val).toBe(1 + 10 + 100) + expect(runs).toBe(1) + + o.val = 200 + expect(runs).toBe(2) + expect(o._val).toBe(200 - 100 - 10) + expect(o.val).toBe(90 + 10 + 100) + }) + + it('supports subclass signal getter/setter overriding base signal getter/setter without super', () => { + class Base { + #v = 1 + @signal get v() { + return this.#v + } + @signal set v(x: number) { + this.#v = x + } + } + + class Sub extends Base { + #y = 100 + @signal override get v() { + return this.#y + } + @signal override set v(x: number) { + this.#y = x + } + } + + const s = new Sub() + let count = 0 + createEffect(() => { + s.v + count++ + }) + + expect(s.v).toBe(100) + expect(count).toBe(1) + + s.v = 50 + expect(s.v).toBe(50) + expect(count).toBe(2) + }) + }) + + describe('invalid usage', () => { + it('throws on duplicate members', () => { + const run = () => { + class SuperDuper { + @signal dupe = 0 + // @ts-expect-error duplicate member + @signal dupe = 0 + } + + new SuperDuper() + } + + // This one works the same way whether compiling with Babel or + // TypeScript. See the same tests for @memo and @effect. + expect(run).toThrow( + '@signal decorated member "dupe" has already been signalified. This can happen if there are duplicated class members.', + ) + }) + }) }) }) diff --git a/src/decorators/signal.ts b/src/decorators/signal.ts index 772c629..4218d13 100644 --- a/src/decorators/signal.ts +++ b/src/decorators/signal.ts @@ -1,14 +1,9 @@ -import {$PROXY} from 'solid-js' -import {getSignal__, trackPropSetAtLeastOnce__, signalify} from '../signals/signalify.js' -import type {SignalMetadata} from './types.js' +import {batch} from 'solid-js' +import {getSignal__, trackPropSetAtLeastOnce__} from '../signals/signalify.js' +import type {AnyObject, ClassySolidMetadata} from './types.js' import type {SignalFunction} from '../signals/createSignalFunction.js' -import { - sortSignalsMemosInMetadata, - isSignalGetter, - getMemberStat, - finalizeMemos, - getSignalsAndMemos, -} from '../_state.js' +import {isSignalGetter, getMemberStat, finalizeMembersIfLast, getMembers, signalifyIfNeeded} from '../_state.js' +import './metadata-shim.js' const Undefined = Symbol() @@ -51,8 +46,8 @@ export function signal( if (context.static) throw new Error('@signal is not supported on static fields yet.') const {kind, name} = context - const metadata = context.metadata as SignalMetadata - const signalsAndMemos = getSignalsAndMemos(metadata) + const metadata = context.metadata as ClassySolidMetadata + const signalsAndMemos = getMembers(metadata) if (!(kind === 'field' || kind === 'accessor' || kind === 'getter' || kind === 'setter')) throw new InvalidSignalDecoratorError() @@ -60,25 +55,20 @@ export function signal( if (kind === 'field') { const stat = getMemberStat(name, 'signal-field', signalsAndMemos) + stat.finalize = function (this: unknown) { + signalifyIfNeeded(this as AnyObject, name, stat) + } + context.addInitializer(function () { - // Special case for Solid proxies: if the object is already a solid proxy, - // all properties are already reactive, no need to signalify. - // @ts-expect-error special indexed access - const proxy = this[$PROXY] as T - if (proxy) return - - sortSignalsMemosInMetadata(metadata) - - if (stat.applied.get(this as object)) return - signalify(this as object, [name as keyof object, (this as object)[name as keyof object]]) - stat.applied.set(this as object, true) - - // If we skipped memoifying prior memo members (accessor and method - // memos) because of prior signal-fields, memo-fields, or - // memo-auto-accessors, finalize those memos now. - finalizeMemos(this as object, stat, signalsAndMemos) + finalizeMembersIfLast(this as AnyObject, signalsAndMemos) }) - } else if (kind === 'accessor') { + } + + // It's ok that getters/setters/auto-accessors are not finalized the same + // way as with fields above and as with memos/effects, because we do the set + // up during decoration which happens well before any initializers (before + // any memos and effects, so these will be tracked). + else if (kind === 'accessor') { const {get, set} = value as {get: () => unknown; set: (v: unknown) => void} const signalStorage = new WeakMap>() let initialValue: unknown = undefined @@ -93,11 +83,15 @@ export function signal( return get.call(this) }, set: function (this: object, newValue: unknown) { - set.call(this, newValue) - trackPropSetAtLeastOnce__(this, name) // not needed anymore? test it - - const s = getSignal__(this, signalStorage, initialValue) - s(typeof newValue === 'function' ? () => newValue : newValue) + // batch, for example in case setter calls super setter, to + // avoid multiple effect runs on a single property set. + batch(() => { + set.call(this, newValue) + trackPropSetAtLeastOnce__(this, name) // not needed anymore? test it + + const s = getSignal__(this, signalStorage, initialValue) + s(typeof newValue === 'function' ? () => newValue : newValue) + }) }, } @@ -108,18 +102,20 @@ export function signal( const getOrSet = value as Function const initialValue = Undefined - if (!Object.hasOwn(metadata, 'getterSetterSignals')) metadata.getterSetterSignals = {} - const signalsStorages = metadata.getterSetterSignals! + if (!Object.hasOwn(metadata, 'classySolid_getterSetterSignals')) metadata.classySolid_getterSetterSignals = {} + const signalsStorages = metadata.classySolid_getterSetterSignals! let signalStorage = signalsStorages[name] if (!signalStorage) signalsStorages[name] = signalStorage = new WeakMap>() - if (!Object.hasOwn(metadata, 'getterSetterPairCounts')) metadata.getterSetterPairCounts = {} - const pairs = metadata.getterSetterPairCounts! + if (!Object.hasOwn(metadata, 'classySolid_getterSetterPairCounts')) metadata.classySolid_getterSetterPairCounts = {} + const pairs = metadata.classySolid_getterSetterPairCounts! // Show a helpful error in case someone forgets to decorate both a getter and setter. queueMicrotask(() => { - if (pairs[name] !== 2) throw new MissingSignalDecoratorError(name) + queueMicrotask(() => delete metadata.classySolid_getterSetterPairCounts) + const missing = pairs[name] !== 2 + if (missing) throw new MissingSignalDecoratorError(name) }) if (kind === 'getter') { @@ -138,13 +134,19 @@ export function signal( pairs[name] ??= 0 pairs[name]++ - return function (this: object, newValue: unknown) { - getOrSet.call(this, newValue) - trackPropSetAtLeastOnce__(this, name) + const newSetter = function (this: object, newValue: unknown) { + // batch, for example in case setter calls super setter, to + // avoid multiple effect runs on a single property set. + batch(() => { + getOrSet.call(this, newValue) + trackPropSetAtLeastOnce__(this, name) - const s = getSignal__(this, signalStorage, initialValue) - s(typeof newValue === 'function' ? () => newValue : newValue) + const s = getSignal__(this, signalStorage, initialValue) + s(typeof newValue === 'function' ? () => newValue : newValue) + }) } + + return newSetter } } } diff --git a/src/decorators/types.ts b/src/decorators/types.ts index 0625a85..acaa056 100644 --- a/src/decorators/types.ts +++ b/src/decorators/types.ts @@ -1,6 +1,8 @@ import type {Constructor} from 'lowclass/dist/Constructor.js' import type {SignalFunction} from '../signals/createSignalFunction.js' +export type AnyObject = Record + export type DecoratedValue = Constructor | Function | ClassAccessorDecoratorTarget | undefined export type PropKey = string | symbol @@ -14,12 +16,27 @@ export interface PropSpec { kind: SupportedKind } -export type SignalOrMemoType = 'signal-field' | 'memo-field' | 'memo-accessor' | 'memo-auto-accessor' | 'memo-method' - -export type SignalMetadata = { - signalFieldsAndMemos?: Array<[key: PropKey, stat: {type: SignalOrMemoType; applied: WeakMap}]> - - getterSetterSignals?: Record> | undefined> +export type SignalOrMemoType = + | 'signal-field' + | 'memo-auto-accessor' + | 'memo-accessor' + | 'memo-method' + | 'effect-auto-accessor' + | 'effect-method' + +export type MetadataMembers = Array + +export type MemberStat = { + type: SignalOrMemoType + name: PropKey + applied: WeakMap + finalize?(this: AnyObject): void + value?: unknown +} - getterSetterPairCounts: {[key: PropKey]: 0 | 1 | 2} +export type ClassySolidMetadata = { + __proto__: ClassySolidMetadata + classySolid_members?: MetadataMembers + classySolid_getterSetterSignals?: Record> | undefined> + classySolid_getterSetterPairCounts?: {[key: PropKey]: 0 | 1 | 2} } diff --git a/src/decorators/untracked.test.ts b/src/decorators/untracked.test.ts index ac9a150..a5fd384 100644 --- a/src/decorators/untracked.test.ts +++ b/src/decorators/untracked.test.ts @@ -86,7 +86,19 @@ describe('Reactivity Tracking in Constructors', () => { it('automatically does not track reactivity in constructors when using @memo', () => { class Foo { - @signal amount = 3 + @signal amount = (() => { + debugger + return 3 + })() + + // @signal accessor yo = 123 + + // @signal get bar() { + // return this + // } + // @signal set bar(v) { + // // do nothing + // } } class Bar extends Foo { diff --git a/src/decorators/untracked.ts b/src/decorators/untracked.ts index f92acb4..eead566 100644 --- a/src/decorators/untracked.ts +++ b/src/decorators/untracked.ts @@ -1,5 +1,6 @@ import type {AnyConstructor} from 'lowclass/dist/Constructor.js' import {getListener, untrack} from 'solid-js' +import './metadata-shim.js' /** * A decorator that makes a class's contructor untracked. diff --git a/src/example.ts b/src/example.ts index 8612b62..09f8bca 100644 --- a/src/example.ts +++ b/src/example.ts @@ -1,48 +1,54 @@ -import {signal, reactive} from './index.js' +import {signal, effect} from './index.js' import {createEffect} from 'solid-js' -@reactive -class Other { - @signal yoo = 'hoo' -} - -new Other() - -@reactive class Foo { @signal foo = 123 @signal get lorem() { return 123 } - set lorem(v) { + @signal set lorem(v) { v } } -@reactive class Bar extends Foo { + // This causes a TDZ error if it comes after bar. See "TDZ" example in signal.test.ts. + // Spec ordering issue: https://github.com/tc39/proposal-decorators/issues/571 + #baz = 789 + @signal bar = 456 - #baz = 789 + // This would cause a TDZ error. + // #baz = 789 - @signal - get baz() { + @signal get baz() { return this.#baz } - set baz(v) { + @signal set baz(v) { this.#baz = v } - constructor() { - super() + @effect logFoo() { + console.log('this.foo:', this.foo) + } + + @effect logLorem() { + console.log('this.lorem:', this.lorem) + } + + @effect logBar() { + console.log('this.bar:', this.bar) + } - console.log('Bar') + @effect logBaz() { + console.log('this.baz:', this.baz) } } export {Foo} +console.log('---------') const b = new Bar() createEffect(() => { diff --git a/src/index.test.ts b/src/index.test.ts index 97312f0..f86896b 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -1,4 +1,6 @@ import {createComputed, createRoot} from 'solid-js' +import {Effectful} from './mixins/index.js' +import {effect, memo, signal, startEffects, stopEffects} from './decorators/index.js' // TODO move type def to @lume/cli, map @types/jest's `expect` type into the // global env. @@ -32,3 +34,149 @@ export function testButterflyProps(b: {colors: number; wingSize: number}, initia expect(b.wingSize).toBe(3, 'incremented wingSize value') expect(count).toBe(3, 'Should be reactive') } + +export class MyElement extends Effectful(HTMLElement) { + static { + customElements.define('my-element', this) + } + + @signal a = 1 + @signal b = 2 + + runs = 0 + result = 0 + + connectedCallback() { + this.createEffect(() => { + this.runs++ + this.result = this.a + this.b + }) + } + + disconnectedCallback() { + this.clearEffects() + } +} + +export class MyElement2 extends Effectful(HTMLElement) { + static { + customElements.define('my-element2', this) + } + + @signal a = 1 + @signal b = 2 + + runs = 0 + result = 0 + + constructor() { + super() + this.createEffect(() => { + this.runs++ + this.result = this.a + this.b + }) + } + + connectedCallback() { + this.startEffects() + } + + disconnectedCallback() { + this.stopEffects() + } +} + +export class MyElement3 extends Effectful(HTMLElement) { + static { + customElements.define('my-element3', this) + } + + runs = 0 + result = 0 + + @signal a = 1 + @signal b = 2 + + @memo get sum() { + return this.a + this.b + } + + @effect log() { + this.runs++ + this.result = this.sum + } + + connectedCallback() { + this.startEffects() + } + + disconnectedCallback() { + this.stopEffects() + } +} +export class MyElement4 extends HTMLElement { + static { + customElements.define('my-element4', this) + } + + runs = 0 + result = 0 + + @signal a = 1 + @signal b = 2 + + @memo get sum() { + return this.a + this.b + } + + @effect log() { + this.runs++ + this.result = this.sum + } + + connectedCallback() { + startEffects(this) + } + + disconnectedCallback() { + stopEffects(this) + } +} + +export function testElementEffects(el: Element & {a: number; b: number; result: number; runs: number}) { + document.body.append(el) // triggers connectedCallback + + expect(el.result).toBe(1 + 2) + expect(el.runs).toBe(1) + + el.a = 5 + expect(el.result).toBe(5 + 2) + expect(el.runs).toBe(2) + + el.b = 10 + expect(el.result).toBe(5 + 10) + expect(el.runs).toBe(3) + + // disconnect the element + + document.body.removeChild(el) // triggers disconnectedCallback + + // Further signal changes do not affect result while disconnected + el.a = 20 + el.b = 30 + expect(el.result).toBe(5 + 10) + expect(el.runs).toBe(3) + + // reconnect the element + document.body.append(el) // triggers connectedCallback + expect(el.result).toBe(20 + 30) + expect(el.runs).toBe(4) + + // further signal changes work again + el.a = 100 + expect(el.result).toBe(100 + 30) + expect(el.runs).toBe(5) + el.b = 200 + expect(el.result).toBe(100 + 200) + expect(el.runs).toBe(6) +} diff --git a/src/mixins/Effectful.test.ts b/src/mixins/Effectful.test.ts new file mode 100644 index 0000000..79cc080 --- /dev/null +++ b/src/mixins/Effectful.test.ts @@ -0,0 +1,282 @@ +import {createSignal} from 'solid-js' +import {Effectful, Effects} from './Effectful.js' +import {signal} from '../decorators/signal.js' +import {MyElement, MyElement2, MyElement3, testElementEffects} from '../index.test.js' + +describe('classy-solid', () => { + describe('Effectful mixin / Effects', () => { + it('createEffect runs immediately, stopEffects stops further runs, startEffects runs effects again', () => { + const [s, setS] = createSignal(1) + const e = new Effects() + + let last = null + let runs = 0 + e.createEffect(() => { + runs++ + last = s() + }) + + expect(last).toBe(1) + expect(runs).toBe(1) + + setS(2) + expect(last).toBe(2) + expect(runs).toBe(2) + + // later, stop effects when done (f.e. when custom element disconnected from DOM)... + + e.stopEffects() + setS(3) + expect(last).toBe(2) + expect(runs).toBe(2) + + // later, start effects again (f.e. when custom element reconnected to DOM)... + + e.startEffects() + expect(last).toBe(3) + expect(runs).toBe(3) + + setS(4) + expect(last).toBe(4) + expect(runs).toBe(4) + + // Clear all effects. + e.clearEffects() + setS(5) + expect(last).toBe(4) + expect(runs).toBe(4) + + e.startEffects() // no effects to start + expect(last).toBe(4) + expect(runs).toBe(4) + setS(6) + expect(last).toBe(4) + expect(runs).toBe(4) + + // Add a new effect after clearing previous ones + + e.createEffect(() => { + runs++ + last = s() + }) + expect(last).toBe(6) + expect(runs).toBe(5) + + setS(7) + expect(last).toBe(7) + expect(runs).toBe(6) + }) + + it('startEffects does not duplicate effects', () => { + const [s, setS] = createSignal(1) + const e = new Effects() + + let runs = 0 + e.createEffect(() => { + runs++ + s() + }) + + expect(runs).toBe(1) + + e.startEffects() // should not duplicate effects + + setS(2) + expect(runs).toBe(2) + }) + + it('clearEffects prevents effects from restarting', () => { + const [s, setS] = createSignal(1) + const e = new Effects() + + let runs = 0 + e.createEffect(() => { + runs++ + s() + }) + + expect(runs).toBe(1) + + e.clearEffects() + + setS(2) + expect(runs).toBe(1) + + e.startEffects() // should not restart any effects + + setS(3) + expect(runs).toBe(1) + }) + + it('can be extended from', () => { + class MyEffects extends Effects { + double = 0 + + constructor() { + super() + this.createEffect(() => { + this.double = this.a * 2 + }) + } + + @signal a = 1 + } + + const me = new MyEffects() + expect(me.double).toBe(2) + + me.a = 5 + expect(me.double).toBe(10) + + me.stopEffects() + me.a = 10 + expect(me.double).toBe(10) + + me.startEffects() + expect(me.double).toBe(20) + }) + + it('works with multiple Effectful-derived classes', () => { + class Base extends Effectful(Object) { + @signal baseSignal = 1 + baseValue = 0 + + constructor() { + super() + this.createEffect(() => { + this.baseValue = this.baseSignal * 10 + }) + } + } + + class Derived extends Base { + @signal derivedSignal = 2 + derivedValue = 0 + + constructor() { + super() + this.createEffect(() => { + this.derivedValue = this.derivedSignal * 100 + }) + } + } + + const d = new Derived() + expect(d.baseValue).toBe(10) + expect(d.derivedValue).toBe(200) + + d.baseSignal = 3 + expect(d.baseValue).toBe(30) + + d.derivedSignal = 4 + expect(d.derivedValue).toBe(400) + + d.stopEffects() + d.baseSignal = 5 + d.derivedSignal = 6 + expect(d.baseValue).toBe(30) + expect(d.derivedValue).toBe(400) + + d.startEffects() + expect(d.baseValue).toBe(50) + expect(d.derivedValue).toBe(600) + }) + + it('supports instanceof checks', () => { + class MyEffectful extends Effectful(Object) {} + + const me = new MyEffectful() + expect(me instanceof Effectful).toBe(true) + expect(me instanceof MyEffectful).toBe(true) + + const e = new Effects() + expect(e instanceof Effects).toBe(true) + expect(e instanceof Effectful).toBe(true) + }) + + it('allows nested createEffect calls', () => { + const [a, setA] = createSignal(0) + const [b, setB] = createSignal(0) + const e = new Effects() + + let outerRuns = 0 + let innerRuns = 0 + + e.createEffect(function outer() { + outerRuns++ + a() + + e.createEffect(function inner() { + innerRuns++ + b() + }) + }) + + expect(outerRuns).toBe(1) + expect(innerRuns).toBe(1) + + e.startEffects() // should not duplicate effects (already started) + + expect(outerRuns).toBe(1) + expect(innerRuns).toBe(1) + + setA(1) + expect(outerRuns).toBe(2) + expect(innerRuns).toBe(2) // inner effect runs because outer effect re-ran + + setB(1) + expect(outerRuns).toBe(2) + expect(innerRuns).toBe(3) // inner effect runs independently + + e.stopEffects() + + expect(outerRuns).toBe(2) + expect(innerRuns).toBe(3) + + e.startEffects() + + expect(outerRuns).toBe(3) + expect(innerRuns).toBe(4) // inner effect runs because outer effect re-ran + + setB(2) + expect(outerRuns).toBe(3) + expect(innerRuns).toBe(5) // inner effect runs independently + }) + + describe('invalid usages', () => { + it('prevents multiple Effectful mixin applications', () => { + expect(() => { + class Base extends Effectful(Object) {} + class Derived extends Effectful(Base) {} + Derived + }).toThrow('Class already extends Effectful, no need to apply the mixin again.') + }) + }) + + describe('usage with custom elements', () => { + it('createEffect in connectedCallback, clearEffects in disconnectedCallback', () => { + const el = document.createElement('my-element') as MyElement + expect(el.result).toBe(0) + expect(el.runs).toBe(0) + + testElementEffects(el) + }) + + it('createEffect in constructor, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => { + const el = document.createElement('my-element2') as MyElement2 + expect(el.result).toBe(1 + 2) + expect(el.runs).toBe(1) // already ran in constructor + + testElementEffects(el) + }) + + it('@effect methods, startEffects in connectedCallback, stopEffects in disconnectedCallback', () => { + const el = document.createElement('my-element3') as MyElement3 + expect(el.result).toBe(1 + 2) + expect(el.runs).toBe(1) // already ran in constructor + + testElementEffects(el) + }) + }) + }) +}) diff --git a/src/mixins/Effectful.ts b/src/mixins/Effectful.ts index 041df02..a73eed0 100644 --- a/src/mixins/Effectful.ts +++ b/src/mixins/Effectful.ts @@ -1,6 +1,7 @@ -import {type Owner, createEffect, onCleanup, createRoot, getOwner, runWithOwner} from 'solid-js' +import {type Owner, createEffect, createRoot, getOwner, runWithOwner} from 'solid-js' import type {AnyConstructor} from 'lowclass/dist/Constructor.js' -import {createStoppableEffect, type Effect} from '../effects/createStoppableEffect.js' + +const isInstance = Symbol() /** * @class Effectful - @@ -13,22 +14,24 @@ import {createStoppableEffect, type Effect} from '../effects/createStoppableEffe * Example: * * ```js - * import {reactive, signal} from 'classy-solid' + * import {signal} from 'classy-solid' * import {foo} from 'somewhere' - * import {bar} from 'elsewhere' + * import {BaseClass} from 'some-lib' * * class MyClass extends Effectful(BaseClass) { + * @signal bar = 0 + * * constructor() { * super() * * // Log `foo` and `bar` any time either of them change. * this.createEffect(() => { - * console.log('foo, bar:', foo(), bar()) + * console.log('foo, bar:', foo(), this.bar) * }) * * // Log only `bar` any time it changes. * this.createEffect(() => { - * console.log('bar:', bar()) + * console.log('bar:', this.bar) * }) * } * @@ -38,98 +41,176 @@ import {createStoppableEffect, type Effect} from '../effects/createStoppableEffe * } * } * ``` + * + * This pairs nicely with the `@effect` decorator. The previous example could be + * rewritten as: + * + * ```js + * import {signal, effect} from 'classy-solid' + * import {foo} from 'somewhere' + * import {BaseClass} from 'some-lib' + * + * class MyClass extends Effectful(BaseClass) { + * @signal bar = 0 + * + * @effect logFooBar() { + * console.log('foo, bar:', foo(), this.bar) + * } + * + * @effect logBar() { + * console.log('bar:', this.bar) + * } + * + * dispose() { + * // Later, stop both of the effects. + * this.stopEffects() + * } + * } + * ``` */ export function Effectful(Base: T) { - return class Effectful extends Base { - #effects = new Set() + if (Base.prototype instanceof Effectful) + throw new Error('Class already extends Effectful, no need to apply the mixin again.') + + class EffectfulClass extends Base { + #effectFunctions: Array<() => void> = [] + #started = true /** * Create a Solid.js effect. The difference from regular * `createEffect()` is that `this` tracks the effects created, so that * they can all be stopped with `this.stopEffects()`. + */ + createEffect(fn: () => void) { + this.startEffects() + this.#createEffect(fn) + } + + #isRestarting = false + + /** + * Start all effects again. This will recreate all effects that were + * previously created with `createEffect()` and stopped with `stopEffects()`. + * + * Example with a custom element class using the @effect decorator: * - * Effects can also be stopped or resumed individually: + * ```ts + * const [someSignal, setSomeSignal] = createSignal(0) * - * ```js - * const effect1 = this.createEffect(() => {...}) - * const effect2 = this.createEffect(() => {...}) + * class MyElement extends Effectful(HTMLElement) { + * @effect logSignal() { + * console.log('someSignal:', someSignal()) + * } * - * // ...later - * effect1.stop() + * connectedCallback() { + * this.startEffects() + * } * - * // ...later - * effect1.resume() + * disconnectedCallback() { + * this.stopEffects() + * } + * } * ``` + * + * The logging of `someSignal` will happen any time `someSignal` changes + * only while the element is connected, and but not when it is + * disconnected. */ - createEffect(fn: () => void) { - let method = 4 - if (method === 1) this.#createEffect1(fn) // not working, bugs out when inside a Solid render() root, effects stop re-running. https://discord.com/channels/722131463138705510/751355413701591120/1188246668466991134 - if (method === 2) createRoot(() => this.#createEffect1(fn)) // works without nesting, but leaks stopped effects until the parent owner is cleaned up (will never clean up if the parent is running for lifetime of the app). - if (method === 3) queueMicrotask(() => this.#createEffect1(fn)) // works without nesting, without leaks - if (method === 4) this.#createEffect2(fn) // works with nesting, without leaks + startEffects() { + if (this.#started) return + this.#started = true + + // Restart all stored effect functions + this.#isRestarting = true + try { + for (const fn of this.#effectFunctions) this.#createEffect(fn) + } finally { + this.#isRestarting = false + } } /** * Stop all of the effects that were created. */ stopEffects() { - let method = 2 - if (method === 1) this.#stopEffects1() - if (method === 2) this.#stopEffects2() - } - - // Method 1 ////////////////////////////////////////// - // Works fine when not in a parent context, or else currently leaks or has the above mentioned bug while a parent exists. - - #createEffect1(fn: () => void) { - let effect: Effect | null = null + if (!this.#started) return + this.#started = false - effect = createStoppableEffect(() => { - if (effect) this.#effects.add(effect) - // nest the user's effect so that if it re-runs a lot it is not deleting/adding from/to our #effects Set a lot. - createEffect(fn) - onCleanup(() => this.#effects.delete(effect!)) - }) - - this.#effects.add(effect) + this.#dispose?.() } - #stopEffects1() { - for (const effect of this.#effects) effect.stop() + /** + * Stop all effects and clear the stored effect functions. After calling + * this, `startEffects()` will not restart any effects because there are + * none stored. This is useful for cleanup scenarios where you'll make + * new effects using `this.createEffect()` instead of restarting old + * ones, namely for backwards compatibility for example with custom + * elements that may be disconnected and reconnected to the DOM and + * currently call `this.createEffect()` in connectedCallback. Example: + * + * ```ts + * class MyElement extends Effectful(HTMLElement) { + * connectedCallback() { + * // Create any number of effects on connect. + * this.createEffect(() => {...}) + * this.createEffect(() => {...}) + * this.createEffect(() => {...}) + * } + * + * disconnectedCallback() { + * // Clean up all effects on disconnect. + * this.clearEffects() + * } + * } + * ``` + */ + clearEffects() { + this.stopEffects() + this.#effectFunctions = [] } - // Method 2 ////////////////////////////////////////// - // Works, with nesting, no leaks. - #owner: Owner | null = null #dispose: (() => void) | null = null - #createEffect2(fn: () => void) { - if (!this.#owner) { + #createEffect(fn: () => void) { + const owner = getOwner() + + // If nested in an existing owner (f.e. nested effect), delegate to + // regular createEffect. + if (owner) return createEffect(fn) + + // Store top-level effect functions so they can be replayed when + // startEffects() is called + if (!this.#isRestarting) this.#effectFunctions.push(fn) + + // If top-level call either attach to an existing root, or make a + // new one if we don't have one yet. + if (this.#owner) runWithOwner(this.#owner, () => createEffect(fn)) + else { createRoot(dispose => { this.#owner = getOwner() - this.#dispose = dispose - this.#createEffect2(fn) + this.#dispose = () => { + dispose() + this.#owner = null + } + createEffect(fn) }) - } else { - let owner = getOwner() - while (owner && owner !== this.#owner) owner = owner?.owner ?? null - - // this.#owner found in the parents of current owner therefore, - // run with current nested owner like a regular solid - // createEffect() - if (owner === this.#owner) return createEffect(fn) - - // this.#owner wasn't found on the parent owners - // run with this.#owner - runWithOwner(this.#owner, () => createEffect(fn)) } } - - #stopEffects2() { - this.#dispose?.() - } } + + ;(EffectfulClass.prototype as any)[isInstance] = true + + Object.defineProperty(EffectfulClass, Symbol.hasInstance, {value: instanceCheck}) + + return EffectfulClass +} + +Object.defineProperty(Effectful, Symbol.hasInstance, {value: instanceCheck}) + +function instanceCheck(obj: any): boolean { + if (!obj || typeof obj !== 'object') return false + return isInstance in obj } /** diff --git a/src/signals/memoify.ts b/src/signals/memoify.ts index ce12463..d84bb13 100644 --- a/src/signals/memoify.ts +++ b/src/signals/memoify.ts @@ -2,11 +2,17 @@ import {createMemo} from 'solid-js' import {createWritableMemo} from '@solid-primitives/memo' import {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js' import {isMemoGetter, isSignalGetter} from '../_state.js' - -type AnyObject = Record +import type {AnyObject, MemberStat} from '../decorators/types.js' const Undefined = Symbol() +let memberStat: MemberStat | null = null + +/** @private internal only */ +export function setMemoifyMemberStat(stat: MemberStat) { + memberStat = stat +} + /** * Convert properties on an object into Solid.js memoized properties. * @@ -77,33 +83,46 @@ const Undefined = Symbol() */ export function memoify(obj: T): T export function memoify(obj: T, ...props: (keyof T)[]): T -/** This overload is for use by the @memo decorator */ -export function memoify(obj: T, isAutoAccessor: boolean, ...props: (keyof T)[]): T -export function memoify(obj: AnyObject, propOrBoolean?: PropertyKey | boolean, ...props: PropertyKey[]) { - const isAutoAccessor = typeof propOrBoolean === 'boolean' ? propOrBoolean : false - - props = - typeof propOrBoolean === 'boolean' - ? props - : typeof propOrBoolean !== 'undefined' - ? [propOrBoolean, ...props] - : props - +export function memoify(obj: AnyObject, ...props: PropertyKey[]) { // If no props specified, use all keys (including symbols) const keys: PropertyKey[] = props.length ? props : [...Object.keys(obj), ...Object.getOwnPropertySymbols(obj)] + const isAutoAccessor = memberStat?.type === 'memo-auto-accessor' + for (const key of keys) { const descriptor = getInheritedDescriptor(obj, key) - if (!descriptor) continue + if (!descriptor) { + memberStat = null + continue + } // Skip if already memoified or signalified - if (descriptor.get && (isMemoGetter.has(descriptor.get) || isSignalGetter.has(descriptor.get))) continue + if (descriptor.get && (isMemoGetter.has(descriptor.get) || isSignalGetter.has(descriptor.get))) { + memberStat = null + continue + } // Handle methods (function-valued properties) if (typeof descriptor.value === 'function' || isAutoAccessor) { + // Skip base class memoify if a subclass is overriding an auto-accessor memo. + const leafmostMemberValue = isAutoAccessor ? descriptor.get : descriptor.value + if (memberStat && leafmostMemberValue !== memberStat!.value) { + memberStat = null + continue + } + const fn = isAutoAccessor ? descriptor.get?.call(obj) : descriptor.value - if (typeof fn !== 'function') continue + + if (typeof fn !== 'function') { + // Throw in decorator mode only. + if (memberStat) { + memberStat = null + throw new Error(`memo value for "${String(key)}" is not a function: ${fn}`) + } + // Otherwise just skip non-function properties (f.e. using memoify(obj) directly on a plain object, without decorators). + else continue + } const name = fn.name || String(key) let value @@ -133,7 +152,11 @@ export function memoify(obj: AnyObject, propOrBoolean?: PropertyKey | boolean, . } // Handle accessors - else if (descriptor.get) { + else if (descriptor.get || descriptor.set) { + if (!descriptor.get) { + memberStat = null + throw new Error(`Cannot memoify accessor "${String(key)}" without a getter.`) + } let get let set: ((val: unknown) => void) | undefined @@ -151,11 +174,16 @@ export function memoify(obj: AnyObject, propOrBoolean?: PropertyKey | boolean, . enumerable: descriptor.enumerable, }) isMemoGetter.add(get) + } else { + // Throw in decorator mode only. + if (memberStat) { + memberStat = null + throw new Error(`memo value for "${String(key)}" is not a function: ${descriptor.value}`) + } } - - // Skip non-function, non-accessor properties - continue } + memberStat = null + return obj } diff --git a/src/signals/signalify.test.ts b/src/signals/signalify.test.ts index de08dd8..1b94a00 100644 --- a/src/signals/signalify.test.ts +++ b/src/signals/signalify.test.ts @@ -11,8 +11,8 @@ describe('classy-solid', () => { let obj2 = signalify(obj, 'n') expect(obj).toBe(obj2) - obj = createMutable({n: 123}) - obj2 = signalify(obj, 'n') + obj = createMutable({n: 123}) // Returns a Proxy wrapping the original object + obj2 = signalify(obj, 'n') // Should return early with the same Proxy expect(obj).toBe(obj2) }) diff --git a/src/signals/signalify.ts b/src/signals/signalify.ts index 972ce1a..7f22aa2 100644 --- a/src/signals/signalify.ts +++ b/src/signals/signalify.ts @@ -1,5 +1,5 @@ import {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js' -import {$PROXY, untrack} from 'solid-js' +import {$PROXY, batch, untrack} from 'solid-js' import type {PropKey} from '../decorators/types.js' import {createSignalFunction, type SignalFunction} from './createSignalFunction.js' import {isMemoGetter, isSignalGetter} from '../_state.js' @@ -159,11 +159,15 @@ export function createSignalAccessor__( }, set: isAccessor ? function (this: object, newValue: unknown) { - originalSet!.call(this, newValue) - trackPropSetAtLeastOnce__(this, prop) - - const s = getSignal__(this, signalStorage, initialVal) - s(typeof newValue === 'function' ? () => newValue : newValue) + // batch, for example in case setter calls super setter, to + // avoid multiple effect runs on a single property set. + batch(() => { + originalSet!.call(this, newValue) + trackPropSetAtLeastOnce__(this, prop) + + const s = getSignal__(this, signalStorage, initialVal) + s(typeof newValue === 'function' ? () => newValue : newValue) + }) } : function (this: object, newValue: unknown) { trackPropSetAtLeastOnce__(this, prop)