diff --git a/dist/decorators/reactive.d.ts.map b/dist/decorators/reactive.d.ts.map index 0a03d6d..2688b82 100644 --- a/dist/decorators/reactive.d.ts.map +++ b/dist/decorators/reactive.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../../src/decorators/reactive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAahE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,GAAG,SAAS,GAAG,GAAG,CAoD/F"} \ No newline at end of file +{"version":3,"file":"reactive.d.ts","sourceRoot":"","sources":["../../src/decorators/reactive.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAC,cAAc,EAAC,MAAM,8BAA8B,CAAA;AAahE;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,QAAQ,CAAC,KAAK,EAAE,cAAc,EAAE,OAAO,EAAE,qBAAqB,GAAG,SAAS,GAAG,GAAG,CA+D/F"} \ No newline at end of file diff --git a/dist/decorators/reactive.js b/dist/decorators/reactive.js index 4796e72..0512115 100644 --- a/dist/decorators/reactive.js +++ b/dist/decorators/reactive.js @@ -1,4 +1,4 @@ -import { getListener, untrack } from 'solid-js'; +import { getListener, $PROXY, untrack } from 'solid-js'; import { getKey, getPropsToSignalify, resetPropsToSignalify } from './signal.js'; import { getCreateSignalAccessor } from '../signalify.js'; @@ -60,25 +60,34 @@ export function reactive(value, context) { // themselves in the effect, making their intent clear. if (getListener()) untrack(() => instance = Reflect.construct(Class, args, new.target)); // super() else super(...args), instance = this; - for (const [prop, { - initialValue - }] of signalProps) { + + // 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 = instance[$PROXY]; + if (proxy) return instance; + for (const [prop, propSpec] of signalProps) { + const kind = propSpec.kind; + let initialValue = propSpec.initialValue; + // @prod-prune - if (!(hasOwnProperty.call(instance, prop) || hasOwnProperty.call(Class.prototype, prop))) { - throw new Error(`Property "${prop.toString()}" not found on instance of class decorated with \`@reactive\`. Did you forget to use the \`@reactive\` decorator on one of your classes that has a "${prop.toString()}" property decorated with \`@signal\`?`); + if (!(hasOwnProperty.call(instance, prop) || hasOwnProperty.call(Class.prototype, prop))) throw new PropNotFoundError(prop); + const isAccessor = kind === 'getter' || kind === 'setter'; + if (isAccessor) { + const desc = Object.getOwnPropertyDescriptor(Class.prototype, prop); + initialValue = desc.get.call(instance); + // Note, if the kind was field, then the initializer already defined the initialValue. } - - // For now at least, we always override like class fields with - // [[Define]] semantics. Perhaps when @signal is used on a - // getter/setter, we should not override in that case, but patch - // the prototype getter/setter (that'll be a bit of work to - // implement though). - const override = true; - createSignalAccessor(instance, prop, initialValue, override); + createSignalAccessor(isAccessor ? Class.prototype : instance, prop, initialValue); } return instance; } } return ReactiveDecorator; } +class PropNotFoundError extends Error { + constructor(prop) { + super(`Property "${String(prop)}" not found on instance of class decorated with \`@reactive\`. Did you forget to use the \`@reactive\` decorator on one of your classes that has a "${String(prop)}" property decorated with \`@signal\`?`); + } +} //# sourceMappingURL=reactive.js.map \ No newline at end of file diff --git a/dist/decorators/reactive.js.map b/dist/decorators/reactive.js.map index e8d5b01..5daa687 100644 --- a/dist/decorators/reactive.js.map +++ b/dist/decorators/reactive.js.map @@ -1 +1 @@ -{"version":3,"file":"reactive.js","names":["getListener","untrack","getKey","getPropsToSignalify","resetPropsToSignalify","getCreateSignalAccessor","accessKey","createSignalAccessor","hasOwnProperty","Object","prototype","reactive","value","context","kind","TypeError","Class","signalProps","ReactiveDecorator","constructor","args","instance","Reflect","construct","new","target","prop","initialValue","call","Error","toString","override"],"sources":["../../src/decorators/reactive.ts"],"sourcesContent":["import type {AnyConstructor} from 'lowclass/dist/Constructor.js'\nimport {getListener, untrack} from 'solid-js'\nimport {getKey, getPropsToSignalify, resetPropsToSignalify} from './signal.js'\nimport {getCreateSignalAccessor} from '../signalify.js'\n\n/**\n * Access key for classy-solid private internal APIs.\n */\nconst accessKey = getKey()\n\nconst createSignalAccessor = getCreateSignalAccessor()\nconst hasOwnProperty = Object.prototype.hasOwnProperty\n\n/**\n * A decorator that makes a class reactive, allowing it have properties\n * decorated with `@signal` to make those properties reactive Solid signals.\n *\n * Example:\n *\n * ```js\n * import {reactive, signal} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * ⁣@reactive\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 reactive(value: AnyConstructor, context: ClassDecoratorContext | undefined): any {\n\t// context may be undefined when unsing reactive() without decorators\n\tif (typeof value !== 'function' || (context && context.kind !== 'class'))\n\t\tthrow new TypeError('The @reactive decorator is only for use on classes.')\n\n\tconst Class = value\n\tconst signalProps = getPropsToSignalify(accessKey)\n\n\t// For the current class decorated with @reactive, we reset the map, so that\n\t// for the next class decorated with @reactive we track only that next\n\t// class's properties that were decorated with @signal. We do this because\n\t// field decorators do not have access to the class or its prototype.\n\t//\n\t// In the future maybe we can use decorator metadata for this\n\t// (https://github.com/tc39/proposal-decorator-metadata)?\n\tresetPropsToSignalify(accessKey)\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\tfor (const [prop, {initialValue}] of signalProps) {\n\t\t\t\t// @prod-prune\n\t\t\t\tif (!(hasOwnProperty.call(instance, prop) || hasOwnProperty.call(Class.prototype, prop))) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`Property \"${prop.toString()}\" not found on instance of class decorated with \\`@reactive\\`. Did you forget to use the \\`@reactive\\` decorator on one of your classes that has a \"${prop.toString()}\" property decorated with \\`@signal\\`?`,\n\t\t\t\t\t)\n\t\t\t\t}\n\n\t\t\t\t// For now at least, we always override like class fields with\n\t\t\t\t// [[Define]] semantics. Perhaps when @signal is used on a\n\t\t\t\t// getter/setter, we should not override in that case, but patch\n\t\t\t\t// the prototype getter/setter (that'll be a bit of work to\n\t\t\t\t// implement though).\n\t\t\t\tconst override = true\n\n\t\t\t\tcreateSignalAccessor(instance, prop as Exclude, initialValue, override)\n\t\t\t}\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,SAAQC,MAAM,EAAEC,mBAAmB,EAAEC,qBAAqB,QAAO,aAAa;AAC9E,SAAQC,uBAAuB,QAAO,iBAAiB;;AAEvD;AACA;AACA;AACA,MAAMC,SAAS,GAAGJ,MAAM,CAAC,CAAC;AAE1B,MAAMK,oBAAoB,GAAGF,uBAAuB,CAAC,CAAC;AACtD,MAAMG,cAAc,GAAGC,MAAM,CAACC,SAAS,CAACF,cAAc;;AAEtD;AACA;AACA;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,SAASG,QAAQA,CAACC,KAAqB,EAAEC,OAA0C,EAAO;EAChG;EACA,IAAI,OAAOD,KAAK,KAAK,UAAU,IAAKC,OAAO,IAAIA,OAAO,CAACC,IAAI,KAAK,OAAQ,EACvE,MAAM,IAAIC,SAAS,CAAC,qDAAqD,CAAC;EAE3E,MAAMC,KAAK,GAAGJ,KAAK;EACnB,MAAMK,WAAW,GAAGd,mBAAmB,CAACG,SAAS,CAAC;;EAElD;EACA;EACA;EACA;EACA;EACA;EACA;EACAF,qBAAqB,CAACE,SAAS,CAAC;EAEhC,MAAMY,iBAAiB,SAASF,KAAK,CAAC;IACrCG,WAAWA,CAAC,GAAGC,IAAW,EAAE;MAC3B,IAAIC,QAA4B;;MAEhC;MACA;MACA;MACA;MACA;MACA,IAAIrB,WAAW,CAAC,CAAC,EAAEC,OAAO,CAAC,MAAOoB,QAAQ,GAAGC,OAAO,CAACC,SAAS,CAACP,KAAK,EAAEI,IAAI,EAAEI,GAAG,CAACC,MAAM,CAAE,CAAC,EAAC;MAAA,KACrF,KAAK,CAAC,GAAGL,IAAI,CAAC,EAAGC,QAAQ,GAAG,IAAK;MAEtC,KAAK,MAAM,CAACK,IAAI,EAAE;QAACC;MAAY,CAAC,CAAC,IAAIV,WAAW,EAAE;QACjD;QACA,IAAI,EAAET,cAAc,CAACoB,IAAI,CAACP,QAAQ,EAAEK,IAAI,CAAC,IAAIlB,cAAc,CAACoB,IAAI,CAACZ,KAAK,CAACN,SAAS,EAAEgB,IAAI,CAAC,CAAC,EAAE;UACzF,MAAM,IAAIG,KAAK,CACd,aAAaH,IAAI,CAACI,QAAQ,CAAC,CAAC,uJAAuJJ,IAAI,CAACI,QAAQ,CAAC,CAAC,wCACnM,CAAC;QACF;;QAEA;QACA;QACA;QACA;QACA;QACA,MAAMC,QAAQ,GAAG,IAAI;QAErBxB,oBAAoB,CAACc,QAAQ,EAAEK,IAAI,EAA8CC,YAAY,EAAEI,QAAQ,CAAC;MACzG;MAEA,OAAOV,QAAQ;IAChB;EACD;EAEA,OAAOH,iBAAiB;AACzB","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"reactive.js","names":["getListener","$PROXY","untrack","getKey","getPropsToSignalify","resetPropsToSignalify","getCreateSignalAccessor","accessKey","createSignalAccessor","hasOwnProperty","Object","prototype","reactive","value","context","kind","TypeError","Class","signalProps","ReactiveDecorator","constructor","args","instance","Reflect","construct","new","target","proxy","prop","propSpec","initialValue","call","PropNotFoundError","isAccessor","desc","getOwnPropertyDescriptor","get","Error","String"],"sources":["../../src/decorators/reactive.ts"],"sourcesContent":["import type {AnyConstructor} from 'lowclass/dist/Constructor.js'\nimport {getListener, $PROXY, untrack} from 'solid-js'\nimport {getKey, getPropsToSignalify, resetPropsToSignalify} from './signal.js'\nimport {getCreateSignalAccessor} from '../signalify.js'\n\n/**\n * Access key for classy-solid private internal APIs.\n */\nconst accessKey = getKey()\n\nconst createSignalAccessor = getCreateSignalAccessor()\nconst hasOwnProperty = Object.prototype.hasOwnProperty\n\n/**\n * A decorator that makes a class reactive, allowing it have properties\n * decorated with `@signal` to make those properties reactive Solid signals.\n *\n * Example:\n *\n * ```js\n * import {reactive, signal} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * ⁣@reactive\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 reactive(value: AnyConstructor, context: ClassDecoratorContext | undefined): any {\n\t// context may be undefined when unsing reactive() without decorators\n\tif (typeof value !== 'function' || (context && context.kind !== 'class'))\n\t\tthrow new TypeError('The @reactive decorator is only for use on classes.')\n\n\tconst Class = value\n\tconst signalProps = getPropsToSignalify(accessKey)\n\n\t// For the current class decorated with @reactive, we reset the map, so that\n\t// for the next class decorated with @reactive we track only that next\n\t// class's properties that were decorated with @signal. We do this because\n\t// field decorators do not have access to the class or its prototype.\n\t//\n\t// In the future maybe we can use decorator metadata for this\n\t// (https://github.com/tc39/proposal-decorator-metadata)?\n\tresetPropsToSignalify(accessKey)\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\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 = instance[$PROXY] as T\n\t\t\tif (proxy) return instance\n\n\t\t\tfor (const [prop, propSpec] of signalProps) {\n\t\t\t\tconst kind = propSpec.kind\n\t\t\t\tlet initialValue = propSpec.initialValue\n\n\t\t\t\t// @prod-prune\n\t\t\t\tif (!(hasOwnProperty.call(instance, prop) || hasOwnProperty.call(Class.prototype, prop)))\n\t\t\t\t\tthrow new PropNotFoundError(prop)\n\n\t\t\t\tconst isAccessor = kind === 'getter' || kind === 'setter'\n\n\t\t\t\tif (isAccessor) {\n\t\t\t\t\tconst desc = Object.getOwnPropertyDescriptor(Class.prototype, prop)!\n\t\t\t\t\tinitialValue = desc.get!.call(instance)\n\t\t\t\t\t// Note, if the kind was field, then the initializer already defined the initialValue.\n\t\t\t\t}\n\n\t\t\t\tcreateSignalAccessor(\n\t\t\t\t\tisAccessor ? Class.prototype : instance,\n\t\t\t\t\tprop as Exclude,\n\t\t\t\t\tinitialValue,\n\t\t\t\t)\n\t\t\t}\n\n\t\t\treturn instance\n\t\t}\n\t}\n\n\treturn ReactiveDecorator\n}\n\nclass PropNotFoundError extends Error {\n\tconstructor(prop: PropertyKey) {\n\t\tsuper(\n\t\t\t`Property \"${String(\n\t\t\t\tprop,\n\t\t\t)}\" not found on instance of class decorated with \\`@reactive\\`. Did you forget to use the \\`@reactive\\` decorator on one of your classes that has a \"${String(\n\t\t\t\tprop,\n\t\t\t)}\" property decorated with \\`@signal\\`?`,\n\t\t)\n\t}\n}\n"],"mappings":"AACA,SAAQA,WAAW,EAAEC,MAAM,EAAEC,OAAO,QAAO,UAAU;AACrD,SAAQC,MAAM,EAAEC,mBAAmB,EAAEC,qBAAqB,QAAO,aAAa;AAC9E,SAAQC,uBAAuB,QAAO,iBAAiB;;AAEvD;AACA;AACA;AACA,MAAMC,SAAS,GAAGJ,MAAM,CAAC,CAAC;AAE1B,MAAMK,oBAAoB,GAAGF,uBAAuB,CAAC,CAAC;AACtD,MAAMG,cAAc,GAAGC,MAAM,CAACC,SAAS,CAACF,cAAc;;AAEtD;AACA;AACA;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,SAASG,QAAQA,CAACC,KAAqB,EAAEC,OAA0C,EAAO;EAChG;EACA,IAAI,OAAOD,KAAK,KAAK,UAAU,IAAKC,OAAO,IAAIA,OAAO,CAACC,IAAI,KAAK,OAAQ,EACvE,MAAM,IAAIC,SAAS,CAAC,qDAAqD,CAAC;EAE3E,MAAMC,KAAK,GAAGJ,KAAK;EACnB,MAAMK,WAAW,GAAGd,mBAAmB,CAACG,SAAS,CAAC;;EAElD;EACA;EACA;EACA;EACA;EACA;EACA;EACAF,qBAAqB,CAACE,SAAS,CAAC;EAEhC,MAAMY,iBAAiB,SAASF,KAAK,CAAC;IACrCG,WAAWA,CAAC,GAAGC,IAAW,EAAE;MAC3B,IAAIC,QAA4B;;MAEhC;MACA;MACA;MACA;MACA;MACA,IAAItB,WAAW,CAAC,CAAC,EAAEE,OAAO,CAAC,MAAOoB,QAAQ,GAAGC,OAAO,CAACC,SAAS,CAACP,KAAK,EAAEI,IAAI,EAAEI,GAAG,CAACC,MAAM,CAAE,CAAC,EAAC;MAAA,KACrF,KAAK,CAAC,GAAGL,IAAI,CAAC,EAAGC,QAAQ,GAAG,IAAK;;MAEtC;MACA;MACA;MACA,MAAMK,KAAK,GAAGL,QAAQ,CAACrB,MAAM,CAAM;MACnC,IAAI0B,KAAK,EAAE,OAAOL,QAAQ;MAE1B,KAAK,MAAM,CAACM,IAAI,EAAEC,QAAQ,CAAC,IAAIX,WAAW,EAAE;QAC3C,MAAMH,IAAI,GAAGc,QAAQ,CAACd,IAAI;QAC1B,IAAIe,YAAY,GAAGD,QAAQ,CAACC,YAAY;;QAExC;QACA,IAAI,EAAErB,cAAc,CAACsB,IAAI,CAACT,QAAQ,EAAEM,IAAI,CAAC,IAAInB,cAAc,CAACsB,IAAI,CAACd,KAAK,CAACN,SAAS,EAAEiB,IAAI,CAAC,CAAC,EACvF,MAAM,IAAII,iBAAiB,CAACJ,IAAI,CAAC;QAElC,MAAMK,UAAU,GAAGlB,IAAI,KAAK,QAAQ,IAAIA,IAAI,KAAK,QAAQ;QAEzD,IAAIkB,UAAU,EAAE;UACf,MAAMC,IAAI,GAAGxB,MAAM,CAACyB,wBAAwB,CAAClB,KAAK,CAACN,SAAS,EAAEiB,IAAI,CAAE;UACpEE,YAAY,GAAGI,IAAI,CAACE,GAAG,CAAEL,IAAI,CAACT,QAAQ,CAAC;UACvC;QACD;QAEAd,oBAAoB,CACnByB,UAAU,GAAGhB,KAAK,CAACN,SAAS,GAAGW,QAAQ,EACvCM,IAAI,EACJE,YACD,CAAC;MACF;MAEA,OAAOR,QAAQ;IAChB;EACD;EAEA,OAAOH,iBAAiB;AACzB;AAEA,MAAMa,iBAAiB,SAASK,KAAK,CAAC;EACrCjB,WAAWA,CAACQ,IAAiB,EAAE;IAC9B,KAAK,CACJ,aAAaU,MAAM,CAClBV,IACD,CAAC,uJAAuJU,MAAM,CAC7JV,IACD,CAAC,wCACF,CAAC;EACF;AACD","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/signal.js b/dist/decorators/signal.js index 58dd537..9fad74d 100644 --- a/dist/decorators/signal.js +++ b/dist/decorators/signal.js @@ -77,7 +77,8 @@ export function signal(_, context) { } if (kind === 'field') { props.set(name, { - initialValue: undefined + initialValue: undefined, + kind }); return function (initialValue) { props.get(name).initialValue = initialValue; @@ -85,7 +86,8 @@ export function signal(_, context) { }; } else if (kind === 'getter' || kind === 'setter') { props.set(name, { - initialValue: undefined + initialValue: undefined, + kind }); } else { throw new Error('The @signal decorator is only for use on fields, getters, and setters. Auto accessor support is coming next if there is demand for it.'); diff --git a/dist/decorators/signal.js.map b/dist/decorators/signal.js.map index added4a..79e024a 100644 --- a/dist/decorators/signal.js.map +++ b/dist/decorators/signal.js.map @@ -1 +1 @@ -{"version":3,"file":"signal.js","names":["propsToSignalify","Map","accessKey","getKey","Error","Symbol","getPropsToSignalify","key","resetPropsToSignalify","isMemberDecorator","context","signal","_","kind","name","props","private","static","set","initialValue","undefined","get","queueReactiveDecoratorChecker","checkerQueued","queueMicrotask","Array","from","keys","join"],"sources":["../../src/decorators/signal.ts"],"sourcesContent":["import type {PropKey, PropSpec} from './types.js'\n\nlet propsToSignalify = new Map()\nlet accessKey: symbol | null = null\n\n/**\n * Provides a key for accessing internal APIs. If any other module tries to get\n * this, an error will be thrown, and signal and reactive decorators will not\n * work.\n */\nexport function getKey() {\n\tif (accessKey) throw new Error('Attempted use of classy-solid internals.')\n\taccessKey = Symbol()\n\treturn accessKey\n}\n\n/**\n * This function provides propsToSignalify to only one external module\n * (reactive.ts). The purpose of this is to keep the API private for reactive.ts\n * only, otherwise an error will be thrown that breaks signal/reactive\n * functionality.\n */\nexport function getPropsToSignalify(key: symbol) {\n\tif (key !== accessKey) throw new Error('Attempted use of classy-solid internals.')\n\treturn propsToSignalify\n}\n\n/**\n * Only the module that first gets the key can call this function (it should be\n * reactive.ts)\n */\nexport function resetPropsToSignalify(key: symbol) {\n\tif (key !== accessKey) throw new Error('Attempted use of classy-solid internals.')\n\tpropsToSignalify = new Map()\n}\n\nfunction isMemberDecorator(context: DecoratorContext): context is ClassMemberDecoratorContext {\n\treturn !!('private' in context)\n}\n\n/**\n * @decorator\n * Decorate properties of a class with `@signal` to back them with Solid\n * signals, making them reactive. Don't forget that the class in which `@signal`\n * is used must be decorated with `@reactive`.\n *\n * Related: See the Solid.js `createSignal` API for creating signals.\n *\n * Example:\n *\n * ```js\n * import {reactive, signal} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * ⁣@reactive\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\t_: unknown,\n\tcontext: ClassFieldDecoratorContext | ClassGetterDecoratorContext | ClassSetterDecoratorContext,\n): any {\n\tconst {kind, name} = context\n\tconst props = propsToSignalify\n\n\tif (isMemberDecorator(context)) {\n\t\tif (context.private) throw new Error('@signal is not supported on private fields yet.')\n\t\tif (context.static) throw new Error('@signal is not supported on static fields yet.')\n\t}\n\n\tif (kind === 'field') {\n\t\tprops.set(name, {initialValue: undefined})\n\t\treturn function (this: object, initialValue: unknown) {\n\t\t\tprops.get(name)!.initialValue = initialValue\n\t\t\treturn initialValue\n\t\t}\n\t} else if (kind === 'getter' || kind === 'setter') {\n\t\tprops.set(name, {initialValue: undefined})\n\t} else {\n\t\tthrow new Error(\n\t\t\t'The @signal decorator is only for use on fields, getters, and setters. Auto accessor support is coming next if there is demand for it.',\n\t\t)\n\t}\n\n\t// @prod-prune\n\tqueueReactiveDecoratorChecker(props)\n}\n\nlet checkerQueued = false\n\n/**\n * This throws an error in some cases of an end dev forgetting to decorate a\n * class with `@reactive` if they used `@signal` on that class's fields.\n *\n * This doesn't work all the time, only when the very last class decorated is\n * missing @reactive, but something is better than nothing. There's another\n * similar check performed in the `@reactive` decorator.\n */\nfunction queueReactiveDecoratorChecker(props: Map) {\n\tif (checkerQueued) return\n\tcheckerQueued = true\n\n\tqueueMicrotask(() => {\n\t\tcheckerQueued = false\n\n\t\t// If the refs are still equal, it means @reactive did not run (forgot\n\t\t// to decorate a class that uses @signal with @reactive).\n\t\tif (props === propsToSignalify) {\n\t\t\tthrow new Error(\n\t\t\t\t// Array.from(map.keys()) instead of [...map.keys()] because it breaks in Oculus browser.\n\t\t\t\t`Stray @signal-decorated properties detected: ${Array.from(props.keys()).join(\n\t\t\t\t\t', ',\n\t\t\t\t)}. Did you forget to use the \\`@reactive\\` decorator on a class that has properties decorated with \\`@signal\\`?`,\n\t\t\t)\n\t\t}\n\t})\n}\n"],"mappings":"AAEA,IAAIA,gBAAgB,GAAG,IAAIC,GAAG,CAAoB,CAAC;AACnD,IAAIC,SAAwB,GAAG,IAAI;;AAEnC;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,MAAMA,CAAA,EAAG;EACxB,IAAID,SAAS,EAAE,MAAM,IAAIE,KAAK,CAAC,0CAA0C,CAAC;EAC1EF,SAAS,GAAGG,MAAM,CAAC,CAAC;EACpB,OAAOH,SAAS;AACjB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,mBAAmBA,CAACC,GAAW,EAAE;EAChD,IAAIA,GAAG,KAAKL,SAAS,EAAE,MAAM,IAAIE,KAAK,CAAC,0CAA0C,CAAC;EAClF,OAAOJ,gBAAgB;AACxB;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASQ,qBAAqBA,CAACD,GAAW,EAAE;EAClD,IAAIA,GAAG,KAAKL,SAAS,EAAE,MAAM,IAAIE,KAAK,CAAC,0CAA0C,CAAC;EAClFJ,gBAAgB,GAAG,IAAIC,GAAG,CAAoB,CAAC;AAChD;AAEA,SAASQ,iBAAiBA,CAACC,OAAyB,EAA0C;EAC7F,OAAO,CAAC,EAAE,SAAS,IAAIA,OAAO,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;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,MAAMA,CACrBC,CAAU,EACVF,OAA+F,EACzF;EACN,MAAM;IAACG,IAAI;IAAEC;EAAI,CAAC,GAAGJ,OAAO;EAC5B,MAAMK,KAAK,GAAGf,gBAAgB;EAE9B,IAAIS,iBAAiB,CAACC,OAAO,CAAC,EAAE;IAC/B,IAAIA,OAAO,CAACM,OAAO,EAAE,MAAM,IAAIZ,KAAK,CAAC,iDAAiD,CAAC;IACvF,IAAIM,OAAO,CAACO,MAAM,EAAE,MAAM,IAAIb,KAAK,CAAC,gDAAgD,CAAC;EACtF;EAEA,IAAIS,IAAI,KAAK,OAAO,EAAE;IACrBE,KAAK,CAACG,GAAG,CAACJ,IAAI,EAAE;MAACK,YAAY,EAAEC;IAAS,CAAC,CAAC;IAC1C,OAAO,UAAwBD,YAAqB,EAAE;MACrDJ,KAAK,CAACM,GAAG,CAACP,IAAI,CAAC,CAAEK,YAAY,GAAGA,YAAY;MAC5C,OAAOA,YAAY;IACpB,CAAC;EACF,CAAC,MAAM,IAAIN,IAAI,KAAK,QAAQ,IAAIA,IAAI,KAAK,QAAQ,EAAE;IAClDE,KAAK,CAACG,GAAG,CAACJ,IAAI,EAAE;MAACK,YAAY,EAAEC;IAAS,CAAC,CAAC;EAC3C,CAAC,MAAM;IACN,MAAM,IAAIhB,KAAK,CACd,wIACD,CAAC;EACF;;EAEA;EACAkB,6BAA6B,CAACP,KAAK,CAAC;AACrC;AAEA,IAAIQ,aAAa,GAAG,KAAK;;AAEzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASD,6BAA6BA,CAACP,KAA6B,EAAE;EACrE,IAAIQ,aAAa,EAAE;EACnBA,aAAa,GAAG,IAAI;EAEpBC,cAAc,CAAC,MAAM;IACpBD,aAAa,GAAG,KAAK;;IAErB;IACA;IACA,IAAIR,KAAK,KAAKf,gBAAgB,EAAE;MAC/B,MAAM,IAAII,KAAK;MACd;MACA,gDAAgDqB,KAAK,CAACC,IAAI,CAACX,KAAK,CAACY,IAAI,CAAC,CAAC,CAAC,CAACC,IAAI,CAC5E,IACD,CAAC,gHACF,CAAC;IACF;EACD,CAAC,CAAC;AACH","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"signal.js","names":["propsToSignalify","Map","accessKey","getKey","Error","Symbol","getPropsToSignalify","key","resetPropsToSignalify","isMemberDecorator","context","signal","_","kind","name","props","private","static","set","initialValue","undefined","get","queueReactiveDecoratorChecker","checkerQueued","queueMicrotask","Array","from","keys","join"],"sources":["../../src/decorators/signal.ts"],"sourcesContent":["import type {PropKey, PropSpec} from './types.js'\n\nlet propsToSignalify = new Map()\nlet accessKey: symbol | null = null\n\n/**\n * Provides a key for accessing internal APIs. If any other module tries to get\n * this, an error will be thrown, and signal and reactive decorators will not\n * work.\n */\nexport function getKey() {\n\tif (accessKey) throw new Error('Attempted use of classy-solid internals.')\n\taccessKey = Symbol()\n\treturn accessKey\n}\n\n/**\n * This function provides propsToSignalify to only one external module\n * (reactive.ts). The purpose of this is to keep the API private for reactive.ts\n * only, otherwise an error will be thrown that breaks signal/reactive\n * functionality.\n */\nexport function getPropsToSignalify(key: symbol) {\n\tif (key !== accessKey) throw new Error('Attempted use of classy-solid internals.')\n\treturn propsToSignalify\n}\n\n/**\n * Only the module that first gets the key can call this function (it should be\n * reactive.ts)\n */\nexport function resetPropsToSignalify(key: symbol) {\n\tif (key !== accessKey) throw new Error('Attempted use of classy-solid internals.')\n\tpropsToSignalify = new Map()\n}\n\nfunction isMemberDecorator(context: DecoratorContext): context is ClassMemberDecoratorContext {\n\treturn !!('private' in context)\n}\n\n/**\n * @decorator\n * Decorate properties of a class with `@signal` to back them with Solid\n * signals, making them reactive. Don't forget that the class in which `@signal`\n * is used must be decorated with `@reactive`.\n *\n * Related: See the Solid.js `createSignal` API for creating signals.\n *\n * Example:\n *\n * ```js\n * import {reactive, signal} from 'classy-solid'\n * import {createEffect} from 'solid-js'\n *\n * ⁣@reactive\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\t_: unknown,\n\tcontext: ClassFieldDecoratorContext | ClassGetterDecoratorContext | ClassSetterDecoratorContext,\n): any {\n\tconst {kind, name} = context\n\tconst props = propsToSignalify\n\n\tif (isMemberDecorator(context)) {\n\t\tif (context.private) throw new Error('@signal is not supported on private fields yet.')\n\t\tif (context.static) throw new Error('@signal is not supported on static fields yet.')\n\t}\n\n\tif (kind === 'field') {\n\t\tprops.set(name, {initialValue: undefined, kind})\n\t\treturn function (this: object, initialValue: unknown) {\n\t\t\tprops.get(name)!.initialValue = initialValue\n\t\t\treturn initialValue\n\t\t}\n\t} else if (kind === 'getter' || kind === 'setter') {\n\t\tprops.set(name, {initialValue: undefined, kind})\n\t} else {\n\t\tthrow new Error(\n\t\t\t'The @signal decorator is only for use on fields, getters, and setters. Auto accessor support is coming next if there is demand for it.',\n\t\t)\n\t}\n\n\t// @prod-prune\n\tqueueReactiveDecoratorChecker(props)\n}\n\nlet checkerQueued = false\n\n/**\n * This throws an error in some cases of an end dev forgetting to decorate a\n * class with `@reactive` if they used `@signal` on that class's fields.\n *\n * This doesn't work all the time, only when the very last class decorated is\n * missing @reactive, but something is better than nothing. There's another\n * similar check performed in the `@reactive` decorator.\n */\nfunction queueReactiveDecoratorChecker(props: Map) {\n\tif (checkerQueued) return\n\tcheckerQueued = true\n\n\tqueueMicrotask(() => {\n\t\tcheckerQueued = false\n\n\t\t// If the refs are still equal, it means @reactive did not run (forgot\n\t\t// to decorate a class that uses @signal with @reactive).\n\t\tif (props === propsToSignalify) {\n\t\t\tthrow new Error(\n\t\t\t\t// Array.from(map.keys()) instead of [...map.keys()] because it breaks in Oculus browser.\n\t\t\t\t`Stray @signal-decorated properties detected: ${Array.from(props.keys()).join(\n\t\t\t\t\t', ',\n\t\t\t\t)}. Did you forget to use the \\`@reactive\\` decorator on a class that has properties decorated with \\`@signal\\`?`,\n\t\t\t)\n\t\t}\n\t})\n}\n"],"mappings":"AAEA,IAAIA,gBAAgB,GAAG,IAAIC,GAAG,CAAoB,CAAC;AACnD,IAAIC,SAAwB,GAAG,IAAI;;AAEnC;AACA;AACA;AACA;AACA;AACA,OAAO,SAASC,MAAMA,CAAA,EAAG;EACxB,IAAID,SAAS,EAAE,MAAM,IAAIE,KAAK,CAAC,0CAA0C,CAAC;EAC1EF,SAAS,GAAGG,MAAM,CAAC,CAAC;EACpB,OAAOH,SAAS;AACjB;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA,OAAO,SAASI,mBAAmBA,CAACC,GAAW,EAAE;EAChD,IAAIA,GAAG,KAAKL,SAAS,EAAE,MAAM,IAAIE,KAAK,CAAC,0CAA0C,CAAC;EAClF,OAAOJ,gBAAgB;AACxB;;AAEA;AACA;AACA;AACA;AACA,OAAO,SAASQ,qBAAqBA,CAACD,GAAW,EAAE;EAClD,IAAIA,GAAG,KAAKL,SAAS,EAAE,MAAM,IAAIE,KAAK,CAAC,0CAA0C,CAAC;EAClFJ,gBAAgB,GAAG,IAAIC,GAAG,CAAoB,CAAC;AAChD;AAEA,SAASQ,iBAAiBA,CAACC,OAAyB,EAA0C;EAC7F,OAAO,CAAC,EAAE,SAAS,IAAIA,OAAO,CAAC;AAChC;;AAEA;AACA;AACA;AACA;AACA;AACA;AACA;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,MAAMA,CACrBC,CAAU,EACVF,OAA+F,EACzF;EACN,MAAM;IAACG,IAAI;IAAEC;EAAI,CAAC,GAAGJ,OAAO;EAC5B,MAAMK,KAAK,GAAGf,gBAAgB;EAE9B,IAAIS,iBAAiB,CAACC,OAAO,CAAC,EAAE;IAC/B,IAAIA,OAAO,CAACM,OAAO,EAAE,MAAM,IAAIZ,KAAK,CAAC,iDAAiD,CAAC;IACvF,IAAIM,OAAO,CAACO,MAAM,EAAE,MAAM,IAAIb,KAAK,CAAC,gDAAgD,CAAC;EACtF;EAEA,IAAIS,IAAI,KAAK,OAAO,EAAE;IACrBE,KAAK,CAACG,GAAG,CAACJ,IAAI,EAAE;MAACK,YAAY,EAAEC,SAAS;MAAEP;IAAI,CAAC,CAAC;IAChD,OAAO,UAAwBM,YAAqB,EAAE;MACrDJ,KAAK,CAACM,GAAG,CAACP,IAAI,CAAC,CAAEK,YAAY,GAAGA,YAAY;MAC5C,OAAOA,YAAY;IACpB,CAAC;EACF,CAAC,MAAM,IAAIN,IAAI,KAAK,QAAQ,IAAIA,IAAI,KAAK,QAAQ,EAAE;IAClDE,KAAK,CAACG,GAAG,CAACJ,IAAI,EAAE;MAACK,YAAY,EAAEC,SAAS;MAAEP;IAAI,CAAC,CAAC;EACjD,CAAC,MAAM;IACN,MAAM,IAAIT,KAAK,CACd,wIACD,CAAC;EACF;;EAEA;EACAkB,6BAA6B,CAACP,KAAK,CAAC;AACrC;AAEA,IAAIQ,aAAa,GAAG,KAAK;;AAEzB;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA,SAASD,6BAA6BA,CAACP,KAA6B,EAAE;EACrE,IAAIQ,aAAa,EAAE;EACnBA,aAAa,GAAG,IAAI;EAEpBC,cAAc,CAAC,MAAM;IACpBD,aAAa,GAAG,KAAK;;IAErB;IACA;IACA,IAAIR,KAAK,KAAKf,gBAAgB,EAAE;MAC/B,MAAM,IAAII,KAAK;MACd;MACA,gDAAgDqB,KAAK,CAACC,IAAI,CAACX,KAAK,CAACY,IAAI,CAAC,CAAC,CAAC,CAACC,IAAI,CAC5E,IACD,CAAC,gHACF,CAAC;IACF;EACD,CAAC,CAAC;AACH","ignoreList":[]} \ No newline at end of file diff --git a/dist/decorators/types.d.ts b/dist/decorators/types.d.ts index 7a15947..4c32b64 100644 --- a/dist/decorators/types.d.ts +++ b/dist/decorators/types.d.ts @@ -1,7 +1,9 @@ import type { Constructor } from 'lowclass/dist/Constructor.js'; export type DecoratedValue = Constructor | Function | ClassAccessorDecoratorTarget | undefined; export type PropKey = string | symbol; +export type SupportedKind = 'field' | 'getter' | 'setter'; export interface PropSpec { initialValue: unknown; + kind: SupportedKind; } //# sourceMappingURL=types.d.ts.map \ No newline at end of file diff --git a/dist/decorators/types.d.ts.map b/dist/decorators/types.d.ts.map index 4f3b0c2..27f459c 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;AAE7D,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;AAIrC,MAAM,WAAW,QAAQ;IACxB,YAAY,EAAE,OAAO,CAAA;CACrB"} \ 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;AAE7D,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"} \ No newline at end of file diff --git a/dist/decorators/types.js.map b/dist/decorators/types.js.map index cceaea1..81c7488 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'\n\nexport type DecoratedValue = Constructor | Function | ClassAccessorDecoratorTarget | undefined\n\nexport type PropKey = string | symbol\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}\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'\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"],"mappings":"","ignoreList":[]} \ No newline at end of file diff --git a/dist/index.test.js b/dist/index.test.js index 9820b87..33de2ac 100644 --- a/dist/index.test.js +++ b/dist/index.test.js @@ -111,93 +111,106 @@ describe('classy-solid', () => { expect(runCount).toBe(3); }); }); - describe('@reactive, @signal, and signalify', () => { - it('makes class properties reactive, using class and property/accessor decorators', () => { - var _initClass, _init_colors, _initProto; - let _Butterfly; - class Butterfly { - static { - ({ - e: [_init_colors, _initProto], - c: [_Butterfly, _initClass] - } = _applyDecs(this, [[signal, 3, "wingSize"], [signal, 0, "colors"]], [reactive])); - } - colors = (_initProto(this), _init_colors(this, 3)); - _wingSize = 2; - get wingSize() { - return this._wingSize; - } - set wingSize(s) { - this._wingSize = s; - } - static { - _initClass(); - } + describe('@reactive, @signal', () => { + var _initClass, _init_colors, _initProto; + let _Butterfly; + class Butterfly { + static { + ({ + e: [_init_colors, _initProto], + c: [_Butterfly, _initClass] + } = _applyDecs(this, [[signal, 3, "wingSize"], [signal, 0, "colors"]], [reactive])); + } + colors = (_initProto(this), _init_colors(this, 3)); + _wingSize = 2; + get wingSize() { + return this._wingSize; } + set wingSize(s) { + this._wingSize = s; + } + static { + _initClass(); + } + } + it('makes class fields reactive, using class and field/accessor decorators', () => { const b = new _Butterfly(); testButterflyProps(b); }); - it('maintains reactivity in subclass overridden fields', async () => { - var _initClass2, _init_colors2, _initProto2, _initClass3, _init_colors3, _Butterfly3; - let _Butterfly2; - class Butterfly { - static { - ({ - e: [_init_colors2, _initProto2], - c: [_Butterfly2, _initClass2] - } = _applyDecs(this, [[signal, 3, "wingSize"], [signal, 0, "colors"]], [reactive])); - } - colors = (_initProto2(this), _init_colors2(this, 3)); - _wingSize = 2; - get wingSize() { - return this._wingSize; - } - set wingSize(s) { - this._wingSize = s; - } - static { - _initClass2(); - } + const ensure = it; + ensure('overridden fields work as expected', async () => { + var _initClass2, _init_colors2; + class Mid extends _Butterfly { + colors = 0; } + + // ensure subclass did not interfere with functionality of base class + const b0 = new _Butterfly(); + testProp(b0, 'colors', 3, 4, true); + expect(Object.getOwnPropertyDescriptor(b0, 'colors')?.get?.call(b0) === 4).toBe(true); // accessor descriptor let _SubButterfly; - class SubButterfly extends (_Butterfly3 = _Butterfly2) { + class SubButterfly extends Mid { static { ({ - e: [_init_colors3], - c: [_SubButterfly, _initClass3] - } = _applyDecs(this, [[signal, 0, "colors"]], [reactive], 0, void 0, _Butterfly3)); + e: [_init_colors2], + c: [_SubButterfly, _initClass2] + } = _applyDecs(this, [[signal, 0, "colors"]], [reactive], 0, void 0, Mid)); } - colors = _init_colors3(this, 123); + colors = _init_colors2(this, 123); static { - _initClass3(); + _initClass2(); } } + + // ensure subclass did not interfere with functionality of base class + const m = new Mid(); + testProp(m, 'colors', 0, 1, false); + expect(Object.getOwnPropertyDescriptor(m, 'colors')?.value === 1).toBe(true); // value descriptor + + class SubSubButterfly extends _SubButterfly { + colors = 456; + } const b = new _SubButterfly(); testButterflyProps(b, 123); + const b2 = new SubSubButterfly(); + testProp(b2, 'colors', 456, 654, false); }); + function testProp(o, k, startVal, newVal, reactive = true) { + let count = 0; + createEffect(() => { + o[k]; + count++; + }); + expect(o[k]).toBe(startVal); + expect(count).toBe(1); + o[k] = newVal; // should not be a signal, should not trigger + + expect(o[k]).toBe(newVal); + expect(count).toBe(reactive ? 2 : 1); + } it('does not prevent superclass constructor from receiving subclass constructor args', () => { - var _initClass4, _initClass5, _init_colors4, _initProto3, _Insect2; + var _initClass3, _initClass4, _init_colors3, _initProto2, _Insect2; let _Insect; class Insect { static { - [_Insect, _initClass4] = _applyDecs(this, [], [reactive]).c; + [_Insect, _initClass3] = _applyDecs(this, [], [reactive]).c; } constructor(double) { this.double = double; } static { - _initClass4(); + _initClass3(); } } - let _Butterfly4; + let _Butterfly2; class Butterfly extends (_Insect2 = _Insect) { static { ({ - e: [_init_colors4, _initProto3], - c: [_Butterfly4, _initClass5] + e: [_init_colors3, _initProto2], + c: [_Butterfly2, _initClass4] } = _applyDecs(this, [[signal, 3, "wingSize"], [signal, 0, "colors"]], [reactive], 0, void 0, _Insect2)); } - colors = (_initProto3(this), _init_colors4(this, 3)); + colors = (_initProto2(this), _init_colors3(this, 3)); _wingSize = 2; get wingSize() { return this._wingSize; @@ -209,138 +222,16 @@ describe('classy-solid', () => { super(arg * 2); } static { - _initClass5(); + _initClass4(); } } - const b = new _Butterfly4(4); + const b = new _Butterfly2(4); expect(b.double).toBe(8); testButterflyProps(b); }); - it('makes class properties reactive, not using any decorators, specified in the constructor', () => { - class Butterfly { - colors = 3; - _wingSize = 2; - get wingSize() { - return this._wingSize; - } - set wingSize(s) { - this._wingSize = s; - } - constructor() { - signalify(this, 'colors', 'wingSize'); - } - } - const b = new Butterfly(); - testButterflyProps(b); - - // quick type check: - const b2 = new Butterfly(); - signalify(b2, 'colors', 'wingSize', - // @ts-expect-error "foo" is not a property on Butterfly - 'foo'); - }); - it('makes class properties reactive, with signalify in the constructor', () => { - class Butterfly { - get wingSize() { - return this._wingSize; - } - set wingSize(s) { - this._wingSize = s; - } - constructor() { - this.colors = 3; - this._wingSize = 2; - signalify(this, 'colors', 'wingSize'); - } - } - const b = new Butterfly(); - testButterflyProps(b); - }); - it('works with a function-style class, with signalify in the constructor', () => { - function Butterfly() { - // @ts-ignore - this.colors = 3; - // @ts-ignore - this._wingSize = 2; - - // @ts-ignore no type checking for ES5-style classes. - signalify(this, 'colors', 'wingSize'); - } - Butterfly.prototype = { - get wingSize() { - return this._wingSize; - }, - set wingSize(s) { - this._wingSize = s; - } - }; - - // @ts-ignore - const b = new Butterfly(); - testButterflyProps(b); - }); - it('works with a function-style class, with properties on the prototype, and signalify in constructor', () => { - function Butterfly() { - // @ts-ignore no type checking for ES5-style classes. - signalify(this, 'colors', 'wingSize'); - } - Butterfly.prototype = { - colors: 3, - _wingSize: 2, - get wingSize() { - return this._wingSize; - }, - set wingSize(s) { - this._wingSize = s; - } - }; - - // @ts-ignore no type checking for ES5-style classes. - const b = new Butterfly(); - testButterflyProps(b); - }); - it('can be used on a function-style class, with properties on the prototype, and signalify on the prototype', () => { - function Butterfly() {} - Butterfly.prototype = { - colors: 3, - _wingSize: 2, - get wingSize() { - return this._wingSize; - }, - set wingSize(s) { - this._wingSize = s; - } - }; - signalify(Butterfly.prototype, 'colors', 'wingSize'); - - // @ts-ignore no type checking for ES5-style classes. - const b = new Butterfly(); - testButterflyProps(b); - }); - it('can be used on a function-style class, with properties in the constructor, and signalify on the prototype', () => { - function Butterfly() { - // @ts-ignore - this.colors = 3; - // @ts-ignore - this._wingSize = 2; - } - Butterfly.prototype = { - get wingSize() { - return this._wingSize; - }, - set wingSize(s) { - this._wingSize = s; - } - }; - signalify(Butterfly.prototype, 'colors', 'wingSize'); - - // @ts-ignore - const b = new Butterfly(); - testButterflyProps(b); - }); it('throws an error when @signal is used without @reactive', async () => { expect(() => { - var _init_foo, _initClass6, _init_bar; + var _init_foo, _initClass5, _init_bar; // user forgot to use @reactive here class Foo { static { @@ -354,12 +245,12 @@ describe('classy-solid', () => { static { ({ e: [_init_bar], - c: [_Bar, _initClass6] + c: [_Bar, _initClass5] } = _applyDecs(this, [[signal, 0, "bar"]], [reactive])); } bar = _init_bar(this, 123); static { - _initClass6(); + _initClass5(); } } new _Bar(); @@ -392,7 +283,7 @@ describe('classy-solid', () => { // expect(err.message).toContain('Did you forget') }); it('works with function values', () => { - var _initClass7, _init_do; + var _initClass6, _init_do; let _Doer; // This test ensures that functions are handled propertly, because // if passed without being wrapped to a signal setter it will be @@ -403,12 +294,12 @@ describe('classy-solid', () => { static { ({ e: [_init_do], - c: [_Doer, _initClass7] + c: [_Doer, _initClass6] } = _applyDecs(this, [[signal, 0, "do"]], [reactive])); } do = _init_do(this, null); static { - _initClass7(); + _initClass6(); } } const doer = new _Doer(); @@ -418,97 +309,19 @@ describe('classy-solid', () => { expect(doer.do).toBe(newFunc); expect(doer.do()).toBe(123); }); - describe('signalify', () => { - it('is not tracked inside of an effect to prevent loops', () => { - // Library author provides obj - const obj = { - n: 123 - }; - signalify(obj, 'n'); // library author might signalify obj.n - - // User code: - createEffect(() => { - // 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. - signalify(obj, 'n'); - obj.n = 123; // does not make an infinite loop - - // A deeper effect will be reading the property. - createEffect(() => { - console.log(obj.n); - }); - }); - - // No expectations in this test, the test passes if a maximum - // callstack size error (infinite loop) does not happen. - }); - }); - it('show that signalify causes constructor to be reactive when used manually instead of decorators', () => { - class Foo { - amount = 3; - constructor() { - signalify(this, 'amount'); - } - } - class Bar extends Foo { - double = 0; - constructor() { - super(); - signalify(this, 'double'); - this.double = this.amount * 2; // this tracks access of .amount - } - } - let count = 0; - let b; - createEffect(() => { - b = new Bar(); // tracks .amount - count++; - }); - expect(count).toBe(1); - b.amount = 4; // triggers - - expect(count).toBe(2); - }); - it('show how to manually untrack constructors when not using decorators', () => { - class Foo { - amount = 3; - constructor() { - signalify(this, 'amount'); - } - } - class Bar extends Foo { - double = 0; - constructor() { - super(); - signalify(this, 'double'); - untrack(() => { - this.double = this.amount * 2; - }); - } - } - let count = 0; - let b; - createEffect(() => { - b = new Bar(); // does not track .amount - count++; - }); - expect(count).toBe(1); - b.amount = 4; // will not trigger - - expect(count).toBe(1); - }); it('automatically does not track reactivity in constructors when using decorators', () => { - var _initClass8, _init_amount, _initClass9, _init_double, _Foo2; + var _initClass7, _init_amount, _initClass8, _init_double, _Foo2; let _Foo; class Foo { static { ({ e: [_init_amount], - c: [_Foo, _initClass8] + c: [_Foo, _initClass7] } = _applyDecs(this, [[signal, 0, "amount"]], [reactive])); } amount = _init_amount(this, 3); static { - _initClass8(); + _initClass7(); } } let _Bar2; @@ -516,7 +329,7 @@ describe('classy-solid', () => { static { ({ e: [_init_double], - c: [_Bar2, _initClass9] + c: [_Bar2, _initClass8] } = _applyDecs(this, [[signal, 0, "double"]], [reactive], 0, void 0, _Foo2)); } double = _init_double(this, 0); @@ -525,7 +338,7 @@ describe('classy-solid', () => { this.double = this.amount * 2; // this read of .amount should not be tracked } static { - _initClass9(); + _initClass8(); } } let b; @@ -546,15 +359,291 @@ describe('classy-solid', () => { expect(b).toBe(b2); }); }); + describe('signalify()', () => { + it('returns the same object that was passed in', () => { + let obj = { + n: 123 + }; + let obj2 = signalify(obj, 'n'); + expect(obj).toBe(obj2); + obj = createMutable({ + n: 123 + }); + obj2 = signalify(obj, 'n'); + expect(obj).toBe(obj2); + }); + describe('making objects reactive with signalify()', () => { + it('', () => { + const butterfly = { + colors: 3, + _wingSize: 2, + get wingSize() { + return this._wingSize; + }, + set wingSize(s) { + this._wingSize = s; + } + }; + const b = signalify(butterfly, 'colors', 'wingSize'); + testButterflyProps(b); + + // quick type check: + // @ts-expect-error "foo" is not a property on butterfly + signalify(butterfly, 'colors', 'wingSize', 'foo'); + }); + it('is not tracked inside of an effect to prevent loops', () => { + test(true); + test(false); + function test(signalifyInitially) { + // Library author provides obj + const obj = { + n: 123 + }; + if (signalifyInitially) signalify(obj, 'n'); // library author might signalify obj.n + + // User code: + createEffect(() => { + // 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. + signalify(obj, 'n'); + obj.n = 123; // does not make an infinite loop + + // A deeper effect will be reading the property. + createEffect(() => { + console.log(obj.n); + }); + }); + + // No expectations in this test, the test passes if a maximum + // callstack size error (infinite loop) does not happen. + } + }); + }); + describe('making reactive classes with signalify instead of with decorators', () => { + it('makes class fields reactive, not using any decorators', () => { + class Butterfly { + colors = 3; + _wingSize = 2; + get wingSize() { + return this._wingSize; + } + set wingSize(s) { + this._wingSize = s; + } + constructor() { + signalify(this, 'colors', 'wingSize'); + } + } + const b = new Butterfly(); + testButterflyProps(b); + + // quick type check: + const b2 = new Butterfly(); + // @ts-expect-error "foo" is not a property on Butterfly + signalify(b2, 'colors', 'wingSize', 'foo'); + }); + it('makes constructor properties reactive, not using any decorators', () => { + class Butterfly { + get wingSize() { + return this._wingSize; + } + set wingSize(s) { + this._wingSize = s; + } + constructor() { + this.colors = 3; + this._wingSize = 2; + signalify(this, 'colors', 'wingSize'); + } + } + const b = new Butterfly(); + testButterflyProps(b); + }); + it('works with a function-style class, with signalify in the constructor', () => { + function Butterfly() { + // @ts-ignore + this.colors = 3; + // @ts-ignore + this._wingSize = 2; + + // @ts-ignore no type checking for ES5-style classes. + signalify(this, 'colors', 'wingSize'); + } + Butterfly.prototype = { + get wingSize() { + return this._wingSize; + }, + set wingSize(s) { + this._wingSize = s; + } + }; + + // @ts-ignore + const b = new Butterfly(); + testButterflyProps(b); + }); + it('works with a function-style class, with properties on the prototype, and signalify in constructor', () => { + function Butterfly() { + // @ts-ignore no type checking for ES5-style classes. + signalify(this, 'colors', 'wingSize'); + } + Butterfly.prototype = { + colors: 3, + _wingSize: 2, + get wingSize() { + return this._wingSize; + }, + set wingSize(s) { + this._wingSize = s; + } + }; + + // @ts-ignore no type checking for ES5-style classes. + const b = new Butterfly(); + testButterflyProps(b); + }); + it('can be used on a function-style class, with properties on the prototype, and signalify on the prototype', () => { + function Butterfly() {} + Butterfly.prototype = { + colors: 3, + _wingSize: 2, + get wingSize() { + return this._wingSize; + }, + set wingSize(s) { + this._wingSize = s; + } + }; + signalify(Butterfly.prototype, 'colors', 'wingSize'); + + // @ts-ignore no type checking for ES5-style classes. + const b = new Butterfly(); + testButterflyProps(b); + }); + it('can be used on a function-style class, with properties in the constructor, and signalify on the prototype', () => { + function Butterfly() { + // @ts-ignore + this.colors = 3; + // @ts-ignore + this._wingSize = 2; + } + Butterfly.prototype = { + get wingSize() { + return this._wingSize; + }, + set wingSize(s) { + this._wingSize = s; + } + }; + signalify(Butterfly.prototype, 'colors', 'wingSize'); + + // @ts-ignore + const b = new Butterfly(); + testButterflyProps(b); + }); + it('show that signalify causes constructor to be reactive when used manually instead of decorators', () => { + class Foo { + amount = 3; + constructor() { + signalify(this, 'amount'); + } + } + class Bar extends Foo { + double = 0; + constructor() { + super(); + signalify(this, 'double'); + this.double = this.amount * 2; // this tracks access of .amount + } + } + let count = 0; + let b; + createEffect(() => { + b = new Bar(); // tracks .amount + count++; + }); + expect(count).toBe(1); + b.amount = 4; // triggers + + expect(count).toBe(2); + }); + it('show how to manually untrack constructors when not using decorators', () => { + class Foo { + amount = 3; + constructor() { + signalify(this, 'amount'); + } + } + class Bar extends Foo { + double = 0; + constructor() { + super(); + signalify(this, 'double'); + untrack(() => { + this.double = this.amount * 2; + }); + } + } + let count = 0; + let b; + createEffect(() => { + b = new Bar(); // does not track .amount + count++; + }); + expect(count).toBe(1); + b.amount = 4; // will not trigger + + expect(count).toBe(1); + }); + }); + it('creates signal storage per descriptor+object pair, not per descriptor', () => { + // This ensures we don't accidentally share a signal with multiple + // objects. For example, we don't want a single signal per descriptor + // because if the descriptor is on a prototype object, then that single + // signal will erroneously be used by all objects extending from that + // prototype. + + const a = signalify({ + foo: 0, + name: 'a' + }, 'foo'); + const b = Object.assign(Object.create(a), { + name: 'b' + }); + expect(a.foo).toBe(0); + expect(b.foo).toBe(0); + let countA = 0; + createEffect(() => { + a.foo; + countA++; + }); + let countB = 0; + createEffect(() => { + b.foo; + countB++; + }); + expect(countA).toBe(1); + expect(countB).toBe(1); + a.foo++; + expect(a.foo).toBe(1); + expect(countA).toBe(2); + + // ensure that updating a's foo property did not update b's foo + // property or trigger b's effect, despite that the property is + // defined in a single location on the prototype. + // @ts-ignore + expect(b.foo).toBe(0); + expect(countB).toBe(1); + }); + }); describe('@component', () => { it('allows to define a class using class syntax', () => { - var _initClass10; + var _initClass9; let onMountCalled = false; let onCleanupCalled = false; let _CoolComp; class CoolComp { static { - [_CoolComp, _initClass10] = _applyDecs(this, [], [component]).c; + [_CoolComp, _initClass9] = _applyDecs(this, [], [component]).c; } onMount() { onMountCalled = true; @@ -567,7 +656,7 @@ describe('classy-solid', () => { return html`
hello classes!
`; } static { - _initClass10(); + _initClass9(); } } const root = document.createElement('div'); @@ -582,13 +671,13 @@ describe('classy-solid', () => { // throws on non-class use expect(() => { - var _initProto4; + var _initProto3; class CoolComp { static { - [_initProto4] = _applyDecs(this, [[component, 2, "onMount"]], []).e; + [_initProto3] = _applyDecs(this, [[component, 2, "onMount"]], []).e; } constructor(...args) { - _initProto4(this); + _initProto3(this); } // @ts-ignore onMount() {} @@ -597,13 +686,13 @@ describe('classy-solid', () => { }).toThrow('component decorator should only be used on a class'); }); it('works in tandem with @reactive and @signal for reactivity', async () => { - var _initClass11, _init_foo2, _init_bar2; + var _initClass10, _init_foo2, _init_bar2; let _CoolComp2; class CoolComp { static { ({ e: [_init_foo2, _init_bar2], - c: [_CoolComp2, _initClass11] + c: [_CoolComp2, _initClass10] } = _applyDecs(this, [[signal, 0, "foo"], [signal, 0, "bar"]], [component, reactive])); } foo = _init_foo2(this, 0); @@ -612,7 +701,7 @@ describe('classy-solid', () => { return html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`; } static { - _initClass11(); + _initClass10(); } } const root = document.createElement('div'); @@ -663,13 +752,13 @@ describe('classy-solid', () => { // FIXME not working, the spread doesn't seem to do anything. xit('works with reactive spreads', async () => { - var _initClass12, _init_foo3, _init_bar3; + var _initClass11, _init_foo3, _init_bar3; let _CoolComp3; class CoolComp { static { ({ e: [_init_foo3, _init_bar3], - c: [_CoolComp3, _initClass12] + c: [_CoolComp3, _initClass11] } = _applyDecs(this, [[signal, 0, "foo"], [signal, 0, "bar"]], [component, reactive])); } foo = _init_foo3(this, 0); @@ -678,7 +767,7 @@ describe('classy-solid', () => { return html`
foo: ${() => this.foo}, bar: ${() => this.bar}
`; } static { - _initClass12(); + _initClass11(); } } const root = document.createElement('div'); diff --git a/dist/index.test.js.map b/dist/index.test.js.map index 9b00f5f..f9cbc8a 100644 --- a/dist/index.test.js.map +++ b/dist/index.test.js.map @@ -1 +1 @@ -{"version":3,"file":"index.test.js","names":["createComputed","createEffect","createRoot","createSignal","untrack","createMutable","render","html","createSignalObject","reactive","signalify","createSignalFunction","signal","createDeferredEffect","component","describe","it","num","set","expect","get","toBe","setResult","n","count","foo","runCount","stop","_stop","Promise","resolve","_initClass","_init_colors","_initProto","_Butterfly","Butterfly","e","c","_applyDecs","colors","_wingSize","wingSize","s","b","testButterflyProps","_initClass2","_init_colors2","_initProto2","_initClass3","_init_colors3","_Butterfly3","_Butterfly2","_SubButterfly","SubButterfly","_initClass4","_initClass5","_init_colors4","_initProto3","_Insect2","_Insect","Insect","constructor","double","_Butterfly4","arg","b2","prototype","_init_foo","_initClass6","_init_bar","Foo","_Bar","Bar","bar","toThrow","_initClass7","_init_do","_Doer","Doer","do","doer","newFunc","obj","console","log","amount","_initClass8","_init_amount","_initClass9","_init_double","_Foo2","_Foo","_Bar2","noLoop","not","_initClass10","onMountCalled","onCleanupCalled","_CoolComp","CoolComp","onMount","onCleanup","template","props","root","document","createElement","body","append","dispose","textContent","remove","_initProto4","args","_initClass11","_init_foo2","_init_bar2","_CoolComp2","a","setA","xit","_initClass12","_init_foo3","_init_bar3","_CoolComp3","o","initialColors","n1","num3","n3","undefined","num4","bool","b1","bool2","n4","func","fn","n5","func2","fn2","n6","stringOrFunc1","sf1","sf2","sf3","sf6","sf7","sf8","stringOrFunc2","sf9","sf10","sf11","sf12","sf13","sf14","sf15"],"sources":["../src/index.test.ts"],"sourcesContent":["import {createComputed, createEffect, createRoot, createSignal, untrack} from 'solid-js'\nimport {createMutable} from 'solid-js/store'\nimport {render} from 'solid-js/web'\nimport html from 'solid-js/html'\nimport {\n\tcreateSignalObject,\n\treactive,\n\tsignalify,\n\tcreateSignalFunction,\n\tsignal,\n\tcreateDeferredEffect,\n\tcomponent,\n} from './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\ndescribe('classy-solid', () => {\n\tdescribe('createSignalObject()', () => {\n\t\tit('has gettable and settable values via .get and .set methods', async () => {\n\t\t\tconst num = createSignalObject(0)\n\n\t\t\t// Set the variable's value by passing a value in.\n\t\t\tnum.set(1)\n\t\t\t// Read the variable's value by calling it with no args.\n\t\t\texpect(num.get()).toBe(1)\n\n\t\t\t// increment example:\n\t\t\tconst setResult = num.set(num.get() + 1)\n\t\t\texpect(num.get()).toBe(2)\n\t\t\texpect(setResult).toBe(2)\n\n\t\t\t// Set with a function that accepts the previous value and returns the next value.\n\t\t\tnum.set(n => n + 1)\n\t\t\texpect(num.get()).toBe(3)\n\t\t})\n\t})\n\n\tdescribe('createSignalFunction()', () => {\n\t\tit('has gettable and settable values via a single overloaded function', async () => {\n\t\t\tconst num = createSignalFunction(0)\n\n\t\t\t// Set the variable's value by passing a value in.\n\t\t\tnum(1)\n\t\t\t// Read the variable's value by calling it with no args.\n\t\t\texpect(num()).toBe(1)\n\n\t\t\t// increment example:\n\t\t\tconst setResult = num(num() + 1)\n\t\t\texpect(num()).toBe(2)\n\t\t\texpect(setResult).toBe(2)\n\n\t\t\t// Set with a function that accepts the previous value and returns the next value.\n\t\t\tnum(n => n + 1)\n\t\t\texpect(num()).toBe(3)\n\t\t})\n\t})\n\n\tdescribe('createDeferredEffect()', () => {\n\t\tit('works', async () => {\n\t\t\tconst count = createSignalFunction(0)\n\t\t\tconst foo = createSignalFunction(0)\n\n\t\t\tlet runCount = 0\n\n\t\t\tconst stop = (() => {\n\t\t\t\tlet stop!: () => void\n\n\t\t\t\tcreateRoot(_stop => {\n\t\t\t\t\tstop = _stop\n\n\t\t\t\t\t// Runs once initially after the current root context just\n\t\t\t\t\t// like createEffect, then any time it re-runs due to a\n\t\t\t\t\t// change in a dependency, the re-run will be deferred in\n\t\t\t\t\t// the next microtask and will run only once (not once per\n\t\t\t\t\t// signal that changed)\n\t\t\t\t\tcreateDeferredEffect(() => {\n\t\t\t\t\t\tcount()\n\t\t\t\t\t\tfoo()\n\t\t\t\t\t\trunCount++\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\treturn stop\n\t\t\t})()\n\n\t\t\t// Queues the effect to run in the next microtask\n\t\t\tcount(1)\n\t\t\tcount(2)\n\t\t\tfoo(3)\n\n\t\t\t// Still 1 because the deferred effect didn't run yet, it will in the next microtask.\n\t\t\texpect(runCount).toBe(1)\n\n\t\t\tawait Promise.resolve()\n\n\t\t\t// It ran only once in the previous microtask (batched), not once per signal write.\n\t\t\texpect(runCount).toBe(2)\n\n\t\t\tcount(3)\n\t\t\tcount(4)\n\t\t\tfoo(5)\n\n\t\t\texpect(runCount).toBe(2)\n\n\t\t\tawait Promise.resolve()\n\n\t\t\texpect(runCount).toBe(3)\n\n\t\t\t// Stops the effect from re-running. It can now be garbage collected.\n\t\t\tstop()\n\n\t\t\tcount(3)\n\t\t\tcount(4)\n\t\t\tfoo(5)\n\n\t\t\texpect(runCount).toBe(3)\n\n\t\t\tawait Promise.resolve()\n\n\t\t\t// Still the same because it was stopped, so it didn't run in the\n\t\t\t// macrotask prior to the await.\n\t\t\texpect(runCount).toBe(3)\n\n\t\t\t// Double check just in case (the wrong implementation would make it\n\t\t\t// skip two microtasks before running).\n\t\t\tawait Promise.resolve()\n\t\t\texpect(runCount).toBe(3)\n\t\t})\n\t})\n\n\tdescribe('@reactive, @signal, and signalify', () => {\n\t\tit('makes class properties reactive, using class and property/accessor decorators', () => {\n\t\t\t@reactive\n\t\t\tclass Butterfly {\n\t\t\t\t@signal colors = 3\n\t\t\t\t_wingSize = 2\n\n\t\t\t\t@signal\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t}\n\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\tthis._wingSize = s\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst b = new Butterfly()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tit('maintains reactivity in subclass overridden fields', async () => {\n\t\t\t@reactive\n\t\t\tclass Butterfly {\n\t\t\t\t@signal colors = 3\n\t\t\t\t_wingSize = 2\n\n\t\t\t\t@signal\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t}\n\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\tthis._wingSize = s\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t@reactive\n\t\t\tclass SubButterfly extends Butterfly {\n\t\t\t\t@signal override colors = 123\n\t\t\t}\n\n\t\t\tconst b = new SubButterfly()\n\n\t\t\ttestButterflyProps(b, 123)\n\t\t})\n\n\t\tit('does not prevent superclass constructor from receiving subclass constructor args', () => {\n\t\t\t@reactive\n\t\t\tclass Insect {\n\t\t\t\tconstructor(public double: number) {}\n\t\t\t}\n\n\t\t\t@reactive\n\t\t\tclass Butterfly extends Insect {\n\t\t\t\t@signal colors = 3\n\t\t\t\t_wingSize = 2\n\n\t\t\t\t@signal\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t}\n\t\t\t\tset 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('makes class properties reactive, not using any decorators, specified in the constructor', () => {\n\t\t\tclass Butterfly {\n\t\t\t\tcolors = 3\n\t\t\t\t_wingSize = 2\n\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t}\n\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\tthis._wingSize = s\n\t\t\t\t}\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst b = new Butterfly()\n\n\t\t\ttestButterflyProps(b)\n\n\t\t\t// quick type check:\n\t\t\tconst b2 = new Butterfly()\n\t\t\tsignalify(\n\t\t\t\tb2,\n\t\t\t\t'colors',\n\t\t\t\t'wingSize',\n\t\t\t\t// @ts-expect-error \"foo\" is not a property on Butterfly\n\t\t\t\t'foo',\n\t\t\t)\n\t\t})\n\n\t\tit('makes class properties reactive, with signalify in the constructor', () => {\n\t\t\tclass Butterfly {\n\t\t\t\tcolors: number\n\t\t\t\t_wingSize: number\n\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t}\n\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\tthis._wingSize = s\n\t\t\t\t}\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tthis.colors = 3\n\t\t\t\t\tthis._wingSize = 2\n\n\t\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tconst b = new Butterfly()\n\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tit('works with a function-style class, with signalify in the constructor', () => {\n\t\t\tfunction Butterfly() {\n\t\t\t\t// @ts-ignore\n\t\t\t\tthis.colors = 3\n\t\t\t\t// @ts-ignore\n\t\t\t\tthis._wingSize = 2\n\n\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t}\n\n\t\t\tButterfly.prototype = {\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t},\n\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\tthis._wingSize = s\n\t\t\t\t},\n\t\t\t}\n\n\t\t\t// @ts-ignore\n\t\t\tconst b = new Butterfly()\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tit('works with a function-style class, with properties on the prototype, and signalify in constructor', () => {\n\t\t\tfunction Butterfly() {\n\t\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\t\tsignalify(this, 'colors', 'wingSize')\n\t\t\t}\n\n\t\t\tButterfly.prototype = {\n\t\t\t\tcolors: 3,\n\t\t\t\t_wingSize: 2,\n\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t},\n\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\tthis._wingSize = s\n\t\t\t\t},\n\t\t\t}\n\n\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\tconst b = new Butterfly()\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tit('can be used on a function-style class, with properties on the prototype, and signalify on the prototype', () => {\n\t\t\tfunction Butterfly() {}\n\n\t\t\tButterfly.prototype = {\n\t\t\t\tcolors: 3,\n\t\t\t\t_wingSize: 2,\n\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t},\n\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\tthis._wingSize = s\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tsignalify(Butterfly.prototype, 'colors', 'wingSize')\n\n\t\t\t// @ts-ignore no type checking for ES5-style classes.\n\t\t\tconst b = new Butterfly()\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tit('can be used on a function-style class, with properties in the constructor, and signalify on the prototype', () => {\n\t\t\tfunction Butterfly() {\n\t\t\t\t// @ts-ignore\n\t\t\t\tthis.colors = 3\n\t\t\t\t// @ts-ignore\n\t\t\t\tthis._wingSize = 2\n\t\t\t}\n\n\t\t\tButterfly.prototype = {\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t},\n\t\t\t\tset wingSize(s: number) {\n\t\t\t\t\tthis._wingSize = s\n\t\t\t\t},\n\t\t\t}\n\n\t\t\tsignalify(Butterfly.prototype, 'colors', 'wingSize')\n\n\t\t\t// @ts-ignore\n\t\t\tconst b = new Butterfly()\n\t\t\ttestButterflyProps(b)\n\t\t})\n\n\t\tit('throws an error when @signal is used without @reactive', async () => {\n\t\t\texpect(() => {\n\t\t\t\t// user forgot to use @reactive here\n\t\t\t\tclass Foo {\n\t\t\t\t\t@signal foo = 'hoo'\n\t\t\t\t}\n\n\t\t\t\tFoo\n\n\t\t\t\t@reactive\n\t\t\t\tclass Bar {\n\t\t\t\t\t@signal bar = 123\n\t\t\t\t}\n\n\t\t\t\tnew Bar()\n\t\t\t}).toThrow('Did you forget')\n\n\t\t\t// TODO how to check for an error thrown from a microtask?\n\t\t\t// (window.addEventListener('error') seems not to work)\n\t\t\t//\n\t\t\t// It just won't work, the error seems to never fire here in the\n\t\t\t// tests, but it works fine when testing manually in Chrome.\n\n\t\t\t// const errPromise = new Promise(r => window.addEventListener('error', e => r(e), {once: true}))\n\n\t\t\t// @reactive\n\t\t\t// class Foo {\n\t\t\t// \t@signal foo = 'hoo'\n\t\t\t// }\n\n\t\t\t// Foo\n\n\t\t\t// // user forgot to use @reactive here\n\t\t\t// class Bar {\n\t\t\t// \t@signal bar = 123\n\t\t\t// }\n\n\t\t\t// Bar\n\n\t\t\t// const err = await errPromise\n\n\t\t\t// expect(err.message).toContain('Did you forget')\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\t@reactive\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\tdescribe('signalify', () => {\n\t\t\tit('is not tracked inside of an effect to prevent loops', () => {\n\t\t\t\t// Library author provides obj\n\t\t\t\tconst obj = {n: 123}\n\t\t\t\tsignalify(obj, 'n') // library author might signalify obj.n\n\n\t\t\t\t// User code:\n\t\t\t\tcreateEffect(() => {\n\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\tsignalify(obj, 'n')\n\n\t\t\t\t\tobj.n = 123 // does not make an infinite loop\n\n\t\t\t\t\t// A deeper effect will be reading the property.\n\t\t\t\t\tcreateEffect(() => {\n\t\t\t\t\t\tconsole.log(obj.n)\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\t// No expectations in this test, the test passes if a maximum\n\t\t\t\t// callstack size error (infinite loop) does not happen.\n\t\t\t})\n\t\t})\n\n\t\tit('show that signalify causes constructor to be reactive when used manually instead of decorators', () => {\n\t\t\tclass Foo {\n\t\t\t\tamount = 3\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsignalify(this, 'amount')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclass Bar extends Foo {\n\t\t\t\tdouble = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsuper()\n\t\t\t\t\tsignalify(this, 'double')\n\t\t\t\t\tthis.double = this.amount * 2 // this tracks access of .amount\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet count = 0\n\t\t\tlet b!: Bar\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tb = new Bar() // tracks .amount\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(count).toBe(1)\n\n\t\t\tb.amount = 4 // triggers\n\n\t\t\texpect(count).toBe(2)\n\t\t})\n\n\t\tit('show how to manually untrack constructors when not using decorators', () => {\n\t\t\tclass Foo {\n\t\t\t\tamount = 3\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsignalify(this, 'amount')\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tclass Bar extends Foo {\n\t\t\t\tdouble = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsuper()\n\t\t\t\t\tsignalify(this, 'double')\n\n\t\t\t\t\tuntrack(() => {\n\t\t\t\t\t\tthis.double = this.amount * 2\n\t\t\t\t\t})\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet count = 0\n\t\t\tlet b!: Bar\n\n\t\t\tcreateEffect(() => {\n\t\t\t\tb = new Bar() // does not track .amount\n\t\t\t\tcount++\n\t\t\t})\n\n\t\t\texpect(count).toBe(1)\n\n\t\t\tb.amount = 4 // will not trigger\n\n\t\t\texpect(count).toBe(1)\n\t\t})\n\n\t\tit('automatically does not track reactivity in constructors when using decorators', () => {\n\t\t\t@reactive\n\t\t\tclass Foo {\n\t\t\t\t@signal amount = 3\n\t\t\t}\n\n\t\t\t@reactive\n\t\t\tclass Bar extends Foo {\n\t\t\t\t@signal double = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsuper()\n\t\t\t\t\tthis.double = this.amount * 2 // this read of .amount should not be tracked\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet b: Bar\n\t\t\tlet count = 0\n\n\t\t\tfunction noLoop() {\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tb = new Bar() // this should not track\n\t\t\t\t\tcount++\n\t\t\t\t})\n\t\t\t}\n\n\t\t\texpect(noLoop).not.toThrow()\n\n\t\t\tconst b2 = b!\n\n\t\t\tb!.amount = 4 // hence this should not trigger\n\n\t\t\t// If the effect ran only once initially, not when setting b.colors,\n\t\t\t// then both variables should reference the same instance\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(b!).toBe(b2)\n\t\t})\n\t})\n\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\nfunction testButterflyProps(b: {colors: number; wingSize: 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(b._wingSize).toBe(2, 'ensure the original accessor works')\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(b._wingSize).toBe(3, 'ensure the original accessor works')\n\texpect(count).toBe(3, 'Should be reactive')\n}\n\n//////////////////////////////////////////////////////////////////////////\n// createSignalObject type tests ///////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////\n\n{\n\tconst num = createSignalObject(1)\n\tlet n1: number = num.get()\n\tn1\n\tnum.set(123)\n\tnum.set(n => (n1 = n + 1))\n\t// @ts-expect-error Expected 1 arguments, but got 0. ts(2554)\n\tnum.set()\n\n\tconst num3 = createSignalObject()\n\t// @ts-expect-error Type 'undefined' is not assignable to type 'number'. ts(2322)\n\tlet n3: number = num3.get()\n\tnum3.set(123)\n\tnum3.set(undefined) // ok, accepts undefined\n\t// @ts-expect-error Object is possibly 'undefined'. ts(2532) (the `n` value)\n\tnum3.set(n => (n3 = n + 1))\n\t// num3.set() // ok, accepts undefined // FIXME broke in Solid 1.7.9\n\n\t// @ts-expect-error Argument of type 'boolean' is not assignable to parameter of type 'number'. ts(2345)\n\tconst num4 = createSignalObject(true)\n\n\tconst bool = createSignalObject(true)\n\tlet b1: boolean = bool.get()\n\tb1\n\tbool.set(false)\n\tbool.set(b => (b1 = !b))\n\t// @ts-expect-error Expected 1 arguments, but got 0. ts(2554)\n\tbool.set()\n\n\tconst bool2 = createSignalObject()\n\t// @ts-expect-error Type 'undefined' is not assignable to type 'number'. ts(2322)\n\tlet n4: boolean = bool2.get()\n\tbool2.set(false)\n\tbool2.set(undefined) // ok, accepts undefined\n\tbool2.set(n => (n4 = !n)) // ok because undefined is being converted to boolean\n\t// @ts-expect-error Type 'boolean | undefined' is not assignable to type 'boolean'. ts(2322)\n\tbool2.set(n => (n4 = n))\n\t// bool2.set() // ok, accepts undefined // FIXME try Solid 1.7.9\n\n\tconst func = createSignalObject(() => 1)\n\t// @ts-expect-error 1 is not assignable to function (no overload matches)\n\tfunc.set(() => 1)\n\tfunc.set(() => (): 1 => 1) // ok, set the value to a function\n\tconst fn: () => 1 = func.get() // ok, returns function value\n\tfn\n\tconst n5: 1 = func.get()()\n\tn5\n\n\tconst func2 = createSignalObject<() => number>(() => 1)\n\t// @FIXME-ts-expect-error number is not assignable to function (no overload matches)\n\tfunc2.set(() => 1) // FIXME should be a type error. Try Solid 1.7.9\n\tfunc2.set(() => () => 1) // ok, set the value to a function\n\tconst fn2: () => number = func2.get() // ok, returns function value\n\tfn2\n\tconst n6: number = func2.get()()\n\tn6\n\n\tconst stringOrFunc1 = createSignalObject<(() => number) | string>('')\n\t// @FIXME-ts-expect-error number not assignable to string | (()=>number) | undefined\n\tstringOrFunc1.set(() => 1) // FIXME should be a type error. Try Solid 1.7.9\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf1: () => number = stringOrFunc1.set(() => () => 1)\n\tsf1\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf2: string = stringOrFunc1.set('oh yeah')\n\tsf2\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf3: string = stringOrFunc1.set(() => 'oh yeah')\n\tsf3\n\t// @ts-expect-error cannot set signal to undefined\n\tstringOrFunc1.set()\n\t// @ts-expect-error cannot set signal to undefined\n\tstringOrFunc1.set(undefined)\n\t// @ts-expect-error return value might be string\n\tconst sf6: () => number = stringOrFunc1.get()\n\tsf6\n\tconst sf7: (() => number) | string | undefined = stringOrFunc1.get()\n\tsf7\n\tconst sf8: (() => number) | string = stringOrFunc1.get()\n\tsf8\n\n\tconst stringOrFunc2 = createSignalObject<(() => number) | string>()\n\t// @FIXME-ts-expect-error number not assignable to string | (()=>number) | undefined\n\tstringOrFunc2.set(() => 1) // FIXME should be a type error. Try Solid 1.7.9\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf9: () => number = stringOrFunc2.set(() => () => 1)\n\tsf9\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf10: string = stringOrFunc2.set('oh yeah')\n\tsf10\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf11: string = stringOrFunc2.set(() => 'oh yeah')\n\tsf11\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf12: undefined = stringOrFunc2.set()\n\tsf12\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf13: undefined = stringOrFunc2.set(undefined)\n\tsf13\n\tconst sf14: (() => number) | string | undefined = stringOrFunc2.get()\n\tsf14\n\t// @ts-expect-error return value might be undefined\n\tconst sf15: (() => number) | string = stringOrFunc2.get()\n\tsf15\n}\n\n//////////////////////////////////////////////////////////////////////////\n// createSignalFunction type tests ///////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////\n\n{\n\tconst num = createSignalFunction(1)\n\tlet n1: number = num()\n\tn1\n\tnum(123)\n\tnum(n => (n1 = n + 1))\n\tnum()\n\n\tconst num3 = createSignalFunction()\n\t// @ts-expect-error Type 'undefined' is not assignable to type 'number'. ts(2322)\n\tlet n3: number = num3()\n\tnum3(123)\n\tnum3(undefined) // ok, accepts undefined\n\t// @ts-expect-error Object is possibly 'undefined'. ts(2532) (the `n` value)\n\tnum3(n => (n3 = n + 1))\n\tnum3() // ok, getter\n\n\t// @ts-expect-error Argument of type 'boolean' is not assignable to parameter of type 'number'. ts(2345)\n\tconst num4 = createSignalFunction(true)\n\n\tconst bool = createSignalFunction(true)\n\tlet b1: boolean = bool()\n\tb1\n\tbool(false)\n\tbool(b => (b1 = !b))\n\tbool()\n\n\tconst bool2 = createSignalFunction()\n\t// @ts-expect-error Type 'undefined' is not assignable to type 'number'. ts(2322)\n\tlet n4: boolean = bool2()\n\tbool2(false)\n\tbool2(undefined) // ok, accepts undefined\n\tbool2(n => (n4 = !n)) // ok because undefined is being converted to boolean\n\t// @ts-expect-error Type 'boolean | undefined' is not assignable to type 'boolean'. ts(2322)\n\tbool2(n => (n4 = n))\n\tbool2() // ok, accepts undefined\n\n\tconst func = createSignalFunction(() => 1)\n\t// @ts-expect-error 1 is not assignable to function (no overload matches)\n\tfunc(() => 1)\n\tfunc(() => (): 1 => 1) // ok, set the value to a function\n\tconst fn: () => 1 = func() // ok, returns function value\n\tfn\n\tconst n5: 1 = func()()\n\tn5\n\n\tconst func2 = createSignalFunction<() => number>(() => 1)\n\t// @ts-expect-error number is not assignable to function (no overload matches)\n\tfunc2(() => 1)\n\tfunc2(() => () => 1) // ok, set the value to a function\n\tconst fn2: () => number = func2() // ok, returns function value\n\tfn2\n\tconst n6: number = func2()()\n\tn6\n\n\tconst stringOrFunc1 = createSignalFunction<(() => number) | string>('')\n\t// @ts-expect-error number not assignable to string | (()=>number) | undefined\n\tstringOrFunc1(() => 1)\n\tconst sf1: () => number = stringOrFunc1(() => () => 1)\n\tsf1\n\tconst sf2: string = stringOrFunc1('oh yeah')\n\tsf2\n\tconst sf3: string = stringOrFunc1(() => 'oh yeah')\n\tsf3\n\tstringOrFunc1() // ok, getter\n\t// @ts-expect-error cannot set signal to undefined\n\tstringOrFunc1(undefined)\n\t// @ts-expect-error return value might be string\n\tconst sf6: () => number = stringOrFunc1()\n\tsf6\n\tconst sf7: (() => number) | string | undefined = stringOrFunc1()\n\tsf7\n\tconst sf8: (() => number) | string = stringOrFunc1()\n\tsf8\n\n\tconst stringOrFunc2 = createSignalFunction<(() => number) | string>()\n\t// @ts-expect-error number not assignable to string | (()=>number) | undefined\n\tstringOrFunc2(() => 1)\n\tconst sf9: () => number = stringOrFunc2(() => () => 1)\n\tsf9\n\tconst sf10: string = stringOrFunc2('oh yeah')\n\tsf10\n\tconst sf11: string = stringOrFunc2(() => 'oh yeah')\n\tsf11\n\t// @ts-expect-error 'string | (() => number) | undefined' is not assignable to type 'undefined'.\n\tconst sf12: undefined = stringOrFunc2()\n\tsf12\n\tconst sf13: undefined = stringOrFunc2(undefined)\n\tsf13\n\tconst sf14: (() => number) | string | undefined = stringOrFunc2()\n\tsf14\n\t// @ts-expect-error return value might be undefined\n\tconst sf15: (() => number) | string = stringOrFunc2()\n\tsf15\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"index.test.js","names":["createComputed","createEffect","createRoot","createSignal","untrack","createMutable","render","html","createSignalObject","reactive","signalify","createSignalFunction","signal","createDeferredEffect","component","describe","it","num","set","expect","get","toBe","setResult","n","count","foo","runCount","stop","_stop","Promise","resolve","_initClass","_init_colors","_initProto","_Butterfly","Butterfly","e","c","_applyDecs","colors","_wingSize","wingSize","s","b","testButterflyProps","ensure","_initClass2","_init_colors2","Mid","b0","testProp","Object","getOwnPropertyDescriptor","call","_SubButterfly","SubButterfly","m","value","SubSubButterfly","b2","o","k","startVal","newVal","_initClass3","_initClass4","_init_colors3","_initProto2","_Insect2","_Insect","Insect","constructor","double","_Butterfly2","arg","_init_foo","_initClass5","_init_bar","Foo","_Bar","Bar","bar","toThrow","_initClass6","_init_do","_Doer","Doer","do","doer","newFunc","_initClass7","_init_amount","_initClass8","_init_double","_Foo2","_Foo","amount","_Bar2","noLoop","not","obj","obj2","butterfly","test","signalifyInitially","console","log","prototype","a","name","assign","create","countA","countB","_initClass9","onMountCalled","onCleanupCalled","_CoolComp","CoolComp","onMount","onCleanup","template","props","root","document","createElement","body","append","dispose","textContent","remove","_initProto3","args","_initClass10","_init_foo2","_init_bar2","_CoolComp2","setA","xit","_initClass11","_init_foo3","_init_bar3","_CoolComp3","initialColors","n1","num3","n3","undefined","num4","bool","b1","bool2","n4","func","fn","n5","func2","fn2","n6","stringOrFunc1","sf1","sf2","sf3","sf6","sf7","sf8","stringOrFunc2","sf9","sf10","sf11","sf12","sf13","sf14","sf15"],"sources":["../src/index.test.ts"],"sourcesContent":["import {createComputed, createEffect, createRoot, createSignal, untrack} from 'solid-js'\nimport {createMutable} from 'solid-js/store'\nimport {render} from 'solid-js/web'\nimport html from 'solid-js/html'\nimport {\n\tcreateSignalObject,\n\treactive,\n\tsignalify,\n\tcreateSignalFunction,\n\tsignal,\n\tcreateDeferredEffect,\n\tcomponent,\n} from './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\ndescribe('classy-solid', () => {\n\tdescribe('createSignalObject()', () => {\n\t\tit('has gettable and settable values via .get and .set methods', async () => {\n\t\t\tconst num = createSignalObject(0)\n\n\t\t\t// Set the variable's value by passing a value in.\n\t\t\tnum.set(1)\n\t\t\t// Read the variable's value by calling it with no args.\n\t\t\texpect(num.get()).toBe(1)\n\n\t\t\t// increment example:\n\t\t\tconst setResult = num.set(num.get() + 1)\n\t\t\texpect(num.get()).toBe(2)\n\t\t\texpect(setResult).toBe(2)\n\n\t\t\t// Set with a function that accepts the previous value and returns the next value.\n\t\t\tnum.set(n => n + 1)\n\t\t\texpect(num.get()).toBe(3)\n\t\t})\n\t})\n\n\tdescribe('createSignalFunction()', () => {\n\t\tit('has gettable and settable values via a single overloaded function', async () => {\n\t\t\tconst num = createSignalFunction(0)\n\n\t\t\t// Set the variable's value by passing a value in.\n\t\t\tnum(1)\n\t\t\t// Read the variable's value by calling it with no args.\n\t\t\texpect(num()).toBe(1)\n\n\t\t\t// increment example:\n\t\t\tconst setResult = num(num() + 1)\n\t\t\texpect(num()).toBe(2)\n\t\t\texpect(setResult).toBe(2)\n\n\t\t\t// Set with a function that accepts the previous value and returns the next value.\n\t\t\tnum(n => n + 1)\n\t\t\texpect(num()).toBe(3)\n\t\t})\n\t})\n\n\tdescribe('createDeferredEffect()', () => {\n\t\tit('works', async () => {\n\t\t\tconst count = createSignalFunction(0)\n\t\t\tconst foo = createSignalFunction(0)\n\n\t\t\tlet runCount = 0\n\n\t\t\tconst stop = (() => {\n\t\t\t\tlet stop!: () => void\n\n\t\t\t\tcreateRoot(_stop => {\n\t\t\t\t\tstop = _stop\n\n\t\t\t\t\t// Runs once initially after the current root context just\n\t\t\t\t\t// like createEffect, then any time it re-runs due to a\n\t\t\t\t\t// change in a dependency, the re-run will be deferred in\n\t\t\t\t\t// the next microtask and will run only once (not once per\n\t\t\t\t\t// signal that changed)\n\t\t\t\t\tcreateDeferredEffect(() => {\n\t\t\t\t\t\tcount()\n\t\t\t\t\t\tfoo()\n\t\t\t\t\t\trunCount++\n\t\t\t\t\t})\n\t\t\t\t})\n\n\t\t\t\treturn stop\n\t\t\t})()\n\n\t\t\t// Queues the effect to run in the next microtask\n\t\t\tcount(1)\n\t\t\tcount(2)\n\t\t\tfoo(3)\n\n\t\t\t// Still 1 because the deferred effect didn't run yet, it will in the next microtask.\n\t\t\texpect(runCount).toBe(1)\n\n\t\t\tawait Promise.resolve()\n\n\t\t\t// It ran only once in the previous microtask (batched), not once per signal write.\n\t\t\texpect(runCount).toBe(2)\n\n\t\t\tcount(3)\n\t\t\tcount(4)\n\t\t\tfoo(5)\n\n\t\t\texpect(runCount).toBe(2)\n\n\t\t\tawait Promise.resolve()\n\n\t\t\texpect(runCount).toBe(3)\n\n\t\t\t// Stops the effect from re-running. It can now be garbage collected.\n\t\t\tstop()\n\n\t\t\tcount(3)\n\t\t\tcount(4)\n\t\t\tfoo(5)\n\n\t\t\texpect(runCount).toBe(3)\n\n\t\t\tawait Promise.resolve()\n\n\t\t\t// Still the same because it was stopped, so it didn't run in the\n\t\t\t// macrotask prior to the await.\n\t\t\texpect(runCount).toBe(3)\n\n\t\t\t// Double check just in case (the wrong implementation would make it\n\t\t\t// skip two microtasks before running).\n\t\t\tawait Promise.resolve()\n\t\t\texpect(runCount).toBe(3)\n\t\t})\n\t})\n\n\tdescribe('@reactive, @signal', () => {\n\t\t@reactive\n\t\tclass Butterfly {\n\t\t\t@signal colors = 3\n\n\t\t\t_wingSize = 2\n\n\t\t\t@signal\n\t\t\tget wingSize() {\n\t\t\t\treturn this._wingSize\n\t\t\t}\n\t\t\tset 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/accessor decorators', () => {\n\t\t\tconst b = new Butterfly()\n\n\t\t\ttestButterflyProps(b)\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\t@reactive\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\t@reactive\n\t\t\tclass Insect {\n\t\t\t\tconstructor(public double: number) {}\n\t\t\t}\n\n\t\t\t@reactive\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\n\t\t\t\tget wingSize() {\n\t\t\t\t\treturn this._wingSize\n\t\t\t\t}\n\t\t\t\tset 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('throws an error when @signal is used without @reactive', async () => {\n\t\t\texpect(() => {\n\t\t\t\t// user forgot to use @reactive here\n\t\t\t\tclass Foo {\n\t\t\t\t\t@signal foo = 'hoo'\n\t\t\t\t}\n\n\t\t\t\tFoo\n\n\t\t\t\t@reactive\n\t\t\t\tclass Bar {\n\t\t\t\t\t@signal bar = 123\n\t\t\t\t}\n\n\t\t\t\tnew Bar()\n\t\t\t}).toThrow('Did you forget')\n\n\t\t\t// TODO how to check for an error thrown from a microtask?\n\t\t\t// (window.addEventListener('error') seems not to work)\n\t\t\t//\n\t\t\t// It just won't work, the error seems to never fire here in the\n\t\t\t// tests, but it works fine when testing manually in Chrome.\n\n\t\t\t// const errPromise = new Promise(r => window.addEventListener('error', e => r(e), {once: true}))\n\n\t\t\t// @reactive\n\t\t\t// class Foo {\n\t\t\t// \t@signal foo = 'hoo'\n\t\t\t// }\n\n\t\t\t// Foo\n\n\t\t\t// // user forgot to use @reactive here\n\t\t\t// class Bar {\n\t\t\t// \t@signal bar = 123\n\t\t\t// }\n\n\t\t\t// Bar\n\n\t\t\t// const err = await errPromise\n\n\t\t\t// expect(err.message).toContain('Did you forget')\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\t@reactive\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('automatically does not track reactivity in constructors when using decorators', () => {\n\t\t\t@reactive\n\t\t\tclass Foo {\n\t\t\t\t@signal amount = 3\n\t\t\t}\n\n\t\t\t@reactive\n\t\t\tclass Bar extends Foo {\n\t\t\t\t@signal double = 0\n\n\t\t\t\tconstructor() {\n\t\t\t\t\tsuper()\n\t\t\t\t\tthis.double = this.amount * 2 // this read of .amount should not be tracked\n\t\t\t\t}\n\t\t\t}\n\n\t\t\tlet b: Bar\n\t\t\tlet count = 0\n\n\t\t\tfunction noLoop() {\n\t\t\t\tcreateEffect(() => {\n\t\t\t\t\tb = new Bar() // this should not track\n\t\t\t\t\tcount++\n\t\t\t\t})\n\t\t\t}\n\n\t\t\texpect(noLoop).not.toThrow()\n\n\t\t\tconst b2 = b!\n\n\t\t\tb!.amount = 4 // hence this should not trigger\n\n\t\t\t// If the effect ran only once initially, not when setting b.colors,\n\t\t\t// then both variables should reference the same instance\n\t\t\texpect(count).toBe(1)\n\t\t\texpect(b!).toBe(b2)\n\t\t})\n\t})\n\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\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\tconsole.log(obj.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\tdeclare _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\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\nfunction testButterflyProps(b: {colors: number; wingSize: 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(b._wingSize).toBe(2, 'ensure the original accessor works')\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(b._wingSize).toBe(3, 'ensure the original accessor works')\n\texpect(count).toBe(3, 'Should be reactive')\n}\n\n//////////////////////////////////////////////////////////////////////////\n// createSignalObject type tests ///////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////\n\n{\n\tconst num = createSignalObject(1)\n\tlet n1: number = num.get()\n\tn1\n\tnum.set(123)\n\tnum.set(n => (n1 = n + 1))\n\t// @ts-expect-error Expected 1 arguments, but got 0. ts(2554)\n\tnum.set()\n\n\tconst num3 = createSignalObject()\n\t// @ts-expect-error Type 'undefined' is not assignable to type 'number'. ts(2322)\n\tlet n3: number = num3.get()\n\tnum3.set(123)\n\tnum3.set(undefined) // ok, accepts undefined\n\t// @ts-expect-error Object is possibly 'undefined'. ts(2532) (the `n` value)\n\tnum3.set(n => (n3 = n + 1))\n\t// num3.set() // ok, accepts undefined // FIXME broke in Solid 1.7.9\n\n\t// @ts-expect-error Argument of type 'boolean' is not assignable to parameter of type 'number'. ts(2345)\n\tconst num4 = createSignalObject(true)\n\n\tconst bool = createSignalObject(true)\n\tlet b1: boolean = bool.get()\n\tb1\n\tbool.set(false)\n\tbool.set(b => (b1 = !b))\n\t// @ts-expect-error Expected 1 arguments, but got 0. ts(2554)\n\tbool.set()\n\n\tconst bool2 = createSignalObject()\n\t// @ts-expect-error Type 'undefined' is not assignable to type 'number'. ts(2322)\n\tlet n4: boolean = bool2.get()\n\tbool2.set(false)\n\tbool2.set(undefined) // ok, accepts undefined\n\tbool2.set(n => (n4 = !n)) // ok because undefined is being converted to boolean\n\t// @ts-expect-error Type 'boolean | undefined' is not assignable to type 'boolean'. ts(2322)\n\tbool2.set(n => (n4 = n))\n\t// bool2.set() // ok, accepts undefined // FIXME try Solid 1.7.9\n\n\tconst func = createSignalObject(() => 1)\n\t// @ts-expect-error 1 is not assignable to function (no overload matches)\n\tfunc.set(() => 1)\n\tfunc.set(() => (): 1 => 1) // ok, set the value to a function\n\tconst fn: () => 1 = func.get() // ok, returns function value\n\tfn\n\tconst n5: 1 = func.get()()\n\tn5\n\n\tconst func2 = createSignalObject<() => number>(() => 1)\n\t// @FIXME-ts-expect-error number is not assignable to function (no overload matches)\n\tfunc2.set(() => 1) // FIXME should be a type error. Try Solid 1.7.9\n\tfunc2.set(() => () => 1) // ok, set the value to a function\n\tconst fn2: () => number = func2.get() // ok, returns function value\n\tfn2\n\tconst n6: number = func2.get()()\n\tn6\n\n\tconst stringOrFunc1 = createSignalObject<(() => number) | string>('')\n\t// @FIXME-ts-expect-error number not assignable to string | (()=>number) | undefined\n\tstringOrFunc1.set(() => 1) // FIXME should be a type error. Try Solid 1.7.9\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf1: () => number = stringOrFunc1.set(() => () => 1)\n\tsf1\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf2: string = stringOrFunc1.set('oh yeah')\n\tsf2\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf3: string = stringOrFunc1.set(() => 'oh yeah')\n\tsf3\n\t// @ts-expect-error cannot set signal to undefined\n\tstringOrFunc1.set()\n\t// @ts-expect-error cannot set signal to undefined\n\tstringOrFunc1.set(undefined)\n\t// @ts-expect-error return value might be string\n\tconst sf6: () => number = stringOrFunc1.get()\n\tsf6\n\tconst sf7: (() => number) | string | undefined = stringOrFunc1.get()\n\tsf7\n\tconst sf8: (() => number) | string = stringOrFunc1.get()\n\tsf8\n\n\tconst stringOrFunc2 = createSignalObject<(() => number) | string>()\n\t// @FIXME-ts-expect-error number not assignable to string | (()=>number) | undefined\n\tstringOrFunc2.set(() => 1) // FIXME should be a type error. Try Solid 1.7.9\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf9: () => number = stringOrFunc2.set(() => () => 1)\n\tsf9\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf10: string = stringOrFunc2.set('oh yeah')\n\tsf10\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf11: string = stringOrFunc2.set(() => 'oh yeah')\n\tsf11\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf12: undefined = stringOrFunc2.set()\n\tsf12\n\t// @ts-expect-error FIXME try Solid 1.7.9\n\tconst sf13: undefined = stringOrFunc2.set(undefined)\n\tsf13\n\tconst sf14: (() => number) | string | undefined = stringOrFunc2.get()\n\tsf14\n\t// @ts-expect-error return value might be undefined\n\tconst sf15: (() => number) | string = stringOrFunc2.get()\n\tsf15\n}\n\n//////////////////////////////////////////////////////////////////////////\n// createSignalFunction type tests ///////////////////////////////////////\n//////////////////////////////////////////////////////////////////////////\n\n{\n\tconst num = createSignalFunction(1)\n\tlet n1: number = num()\n\tn1\n\tnum(123)\n\tnum(n => (n1 = n + 1))\n\tnum()\n\n\tconst num3 = createSignalFunction()\n\t// @ts-expect-error Type 'undefined' is not assignable to type 'number'. ts(2322)\n\tlet n3: number = num3()\n\tnum3(123)\n\tnum3(undefined) // ok, accepts undefined\n\t// @ts-expect-error Object is possibly 'undefined'. ts(2532) (the `n` value)\n\tnum3(n => (n3 = n + 1))\n\tnum3() // ok, getter\n\n\t// @ts-expect-error Argument of type 'boolean' is not assignable to parameter of type 'number'. ts(2345)\n\tconst num4 = createSignalFunction(true)\n\n\tconst bool = createSignalFunction(true)\n\tlet b1: boolean = bool()\n\tb1\n\tbool(false)\n\tbool(b => (b1 = !b))\n\tbool()\n\n\tconst bool2 = createSignalFunction()\n\t// @ts-expect-error Type 'undefined' is not assignable to type 'number'. ts(2322)\n\tlet n4: boolean = bool2()\n\tbool2(false)\n\tbool2(undefined) // ok, accepts undefined\n\tbool2(n => (n4 = !n)) // ok because undefined is being converted to boolean\n\t// @ts-expect-error Type 'boolean | undefined' is not assignable to type 'boolean'. ts(2322)\n\tbool2(n => (n4 = n))\n\tbool2() // ok, accepts undefined\n\n\tconst func = createSignalFunction(() => 1)\n\t// @ts-expect-error 1 is not assignable to function (no overload matches)\n\tfunc(() => 1)\n\tfunc(() => (): 1 => 1) // ok, set the value to a function\n\tconst fn: () => 1 = func() // ok, returns function value\n\tfn\n\tconst n5: 1 = func()()\n\tn5\n\n\tconst func2 = createSignalFunction<() => number>(() => 1)\n\t// @ts-expect-error number is not assignable to function (no overload matches)\n\tfunc2(() => 1)\n\tfunc2(() => () => 1) // ok, set the value to a function\n\tconst fn2: () => number = func2() // ok, returns function value\n\tfn2\n\tconst n6: number = func2()()\n\tn6\n\n\tconst stringOrFunc1 = createSignalFunction<(() => number) | string>('')\n\t// @ts-expect-error number not assignable to string | (()=>number) | undefined\n\tstringOrFunc1(() => 1)\n\tconst sf1: () => number = stringOrFunc1(() => () => 1)\n\tsf1\n\tconst sf2: string = stringOrFunc1('oh yeah')\n\tsf2\n\tconst sf3: string = stringOrFunc1(() => 'oh yeah')\n\tsf3\n\tstringOrFunc1() // ok, getter\n\t// @ts-expect-error cannot set signal to undefined\n\tstringOrFunc1(undefined)\n\t// @ts-expect-error return value might be string\n\tconst sf6: () => number = stringOrFunc1()\n\tsf6\n\tconst sf7: (() => number) | string | undefined = stringOrFunc1()\n\tsf7\n\tconst sf8: (() => number) | string = stringOrFunc1()\n\tsf8\n\n\tconst stringOrFunc2 = createSignalFunction<(() => number) | string>()\n\t// @ts-expect-error number not assignable to string | (()=>number) | undefined\n\tstringOrFunc2(() => 1)\n\tconst sf9: () => number = stringOrFunc2(() => () => 1)\n\tsf9\n\tconst sf10: string = stringOrFunc2('oh yeah')\n\tsf10\n\tconst sf11: string = stringOrFunc2(() => 'oh yeah')\n\tsf11\n\t// @ts-expect-error 'string | (() => number) | undefined' is not assignable to type 'undefined'.\n\tconst sf12: undefined = stringOrFunc2()\n\tsf12\n\tconst sf13: undefined = stringOrFunc2(undefined)\n\tsf13\n\tconst sf14: (() => number) | string | undefined = stringOrFunc2()\n\tsf14\n\t// @ts-expect-error return value might be undefined\n\tconst sf15: (() => number) | string = stringOrFunc2()\n\tsf15\n}\n"],"mappings":"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","ignoreList":[]} \ No newline at end of file diff --git a/dist/signalify.d.ts b/dist/signalify.d.ts index 656f0cb..36bca7c 100644 --- a/dist/signalify.d.ts +++ b/dist/signalify.d.ts @@ -47,13 +47,14 @@ * }) * ``` */ -export declare function signalify(obj: T, ...props: (keyof T)[]): T; export declare function signalify(obj: T): T; +export declare function signalify(obj: T, ...props: (keyof T)[]): T; +export declare function signalify(obj: T, ...props: [key: keyof T, initialValue: unknown][]): T; /** * This ensures that `createSignalAccessor` is kept internal to classy-solid only. */ export declare function getCreateSignalAccessor(): typeof createSignalAccessor; export declare function __isPropSetAtLeastOnce(instance: object, prop: string | symbol): boolean; -declare function createSignalAccessor(obj: T, prop: Exclude, initialVal?: unknown, override?: boolean): void; +declare function createSignalAccessor(obj: T, prop: Exclude, initialVal: unknown): void; export {}; //# sourceMappingURL=signalify.d.ts.map \ No newline at end of file diff --git a/dist/signalify.d.ts.map b/dist/signalify.d.ts.map index be6bcbc..79550f3 100644 --- a/dist/signalify.d.ts.map +++ b/dist/signalify.d.ts.map @@ -1 +1 @@ -{"version":3,"file":"signalify.d.ts","sourceRoot":"","sources":["../src/signalify.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;AACH,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,GAAG,KAAK,EAAE,CAAC,MAAM,CAAC,CAAC,EAAE,GAAG,CAAC,CAAA;AAC7E,wBAAgB,SAAS,CAAC,CAAC,SAAS,MAAM,EAAE,CAAC,SAAS,MAAM,CAAC,EAAE,GAAG,EAAE,CAAC,GAAG,CAAC,CAAA;AAczE;;GAEG;AACH,wBAAgB,uBAAuB,gCAItC;AASD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,WAE7E;AASD,iBAAS,oBAAoB,CAAC,CAAC,SAAS,MAAM,EAC7C,GAAG,EAAE,CAAC,EACN,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAG9B,UAAU,GAAE,OAAkC,EAe9C,QAAQ,UAAQ,GACd,IAAI,CA+FN"} \ No newline at end of file +{"version":3,"file":"signalify.d.ts","sourceRoot":"","sources":["../src/signalify.ts"],"names":[],"mappings":"AAMA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAgDG;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,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;AA0BzG;;GAEG;AACH,wBAAgB,uBAAuB,gCAItC;AASD,wBAAgB,sBAAsB,CAAC,QAAQ,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,MAAM,WAE7E;AASD,iBAAS,oBAAoB,CAAC,CAAC,SAAS,MAAM,EAAE,GAAG,EAAE,CAAC,EAAE,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,EAAE,MAAM,CAAC,EAAE,UAAU,EAAE,OAAO,GAAG,IAAI,CA0EjH"} \ No newline at end of file diff --git a/dist/signalify.js b/dist/signalify.js index 709feda..59bcd77 100644 --- a/dist/signalify.js +++ b/dist/signalify.js @@ -1,6 +1,5 @@ import { getInheritedDescriptor } from 'lowclass/dist/getInheritedDescriptor.js'; import { createSignal, $PROXY, untrack } from 'solid-js'; -const signalifiedProps = new WeakMap(); /** * Convert properties on an object into Solid signal-backed properties. @@ -53,9 +52,22 @@ const signalifiedProps = new WeakMap(); */ export function signalify(obj, ...props) { - // We cast from PropertyKey[] to PropKey[] because numbers can't actually be keys, only string | symbol. + // 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 = obj[$PROXY]; + if (proxy) return obj; const _props = props.length ? props : Object.keys(obj).concat(Object.getOwnPropertySymbols(obj)); - for (const prop of _props) createSignalAccessor(obj, prop); + + // Use `untrack` here to be extra safe the initial value doesn't count as a + // dependency and cause a reactivity loop. + for (const prop of _props) { + const isTuple = Array.isArray(prop); + // We cast from PropertyKey to PropKey because keys can't actually be number, only string | symbol. + const _prop = isTuple ? prop[0] : prop; + const initialValue = isTuple ? prop[1] : untrack(() => obj[_prop]); + createSignalAccessor(obj, _prop, initialValue); + } return obj; } let gotCreateSignalAccessor = false; @@ -84,50 +96,17 @@ function trackPropSetAtLeastOnce(instance, prop) { propsSetAtLeastOnce.get(instance).add(prop); } const isSignalGetter = new WeakSet(); -function createSignalAccessor(obj, prop, -// Untrack here to be extra safe this doesn't count as a dependency and -// cause a reactivity loop. -initialVal = untrack(() => obj[prop]), -// If an object already has a particular signalified property, override it -// with a new one anyway (useful for maintaining consistency with class -// inheritance where class fields always override fields from base classes -// due to their [[Define]] semantics). False is a good default for signalify() -// usage where someone is augmenting an existing object, but true is more -// useful with usage of @signal on class fields. -// -// Note that if @signal were to specify this as false, it would cause -// @signal-decorated subclass fields to override base class -// @signal-decorated fields with a new value descriptor but without -// signalifiying the field, effectively disabling reactivity, which is a bug -// (a field decorated with @signal *must* be reactive). The test named -// "maintains reactivity in subclass overridden fields" was added to ensure -// that the subclass use case works. -override = false) { - if (!override && signalifiedProps.get(obj)?.has(prop)) return; - - // 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 = obj[$PROXY]; - if (proxy) return; +function createSignalAccessor(obj, prop, initialVal) { let descriptor = getInheritedDescriptor(obj, prop); let originalGet; let originalSet; if (descriptor) { originalGet = descriptor.get; originalSet = descriptor.set; - - // Even if override is true, if we have a signal accessor, there's no - // need to replace it with another signal accessor. We only need to - // override when the current descriptor is not a signal accessor. - // TODO this needs tests. if (originalGet && isSignalGetter.has(originalGet)) return; if (originalGet || originalSet) { // reactivity requires both - if (!originalGet || !originalSet) { - console.warn(`The \`@signal\` decorator was used on an accessor named "${prop.toString()}" which had a getter or a setter, but not both. Reactivity on accessors works only when accessors have both get and set. In this case the decorator does not do anything.`); - return; - } + if (!originalGet || !originalSet) return warnNotReadWrite(prop); delete descriptor.get; delete descriptor.set; } else { @@ -139,25 +118,22 @@ override = false) { // if it isn't writable, we don't need to make a reactive variable because // the value won't change - if (!descriptor.writable) { - console.warn(`The \`@signal\` decorator was used on a property named "${prop.toString()}" that is not writable. Reactivity is not enabled for non-writable properties.`); - return; - } + if (!descriptor.writable) return warnNotWritable(prop); delete descriptor.value; delete descriptor.writable; } } - const s = createSignal(initialVal, { - equals: false - }); + const signalStorage = new WeakMap(); descriptor = { configurable: true, enumerable: true, ...descriptor, get: originalGet ? function () { + const s = getSignal(this, signalStorage, initialVal); s[0](); // read return originalGet.call(this); } : function () { + const s = getSignal(this, signalStorage, initialVal); return s[0](); // read }, set: originalSet ? function (newValue) { @@ -165,17 +141,30 @@ override = false) { trackPropSetAtLeastOnce(this, prop); // write + const s = getSignal(this, signalStorage, initialVal); if (typeof newValue === 'function') s[1](() => newValue);else s[1](newValue); } : function (newValue) { trackPropSetAtLeastOnce(this, prop); // write + const s = getSignal(this, signalStorage, initialVal); if (typeof newValue === 'function') s[1](() => newValue);else s[1](newValue); } }; isSignalGetter.add(descriptor.get); Object.defineProperty(obj, prop, descriptor); - if (!signalifiedProps.has(obj)) signalifiedProps.set(obj, new Set()); - signalifiedProps.get(obj).add(prop); +} +function getSignal(obj, storage, initialVal) { + let s = storage.get(obj); + if (!s) storage.set(obj, s = createSignal(initialVal, { + equals: false + })); + return s; +} +function warnNotReadWrite(prop) { + console.warn(`Cannot signalify property named "${String(prop)}" which had a getter or a setter, but not both. Reactivity on accessors works only when accessors have both get and set. Skipped.`); +} +function warnNotWritable(prop) { + console.warn(`The \`@signal\` decorator was used on a property named "${String(prop)}" that is not writable. Reactivity is not enabled for non-writable properties.`); } //# sourceMappingURL=signalify.js.map \ No newline at end of file diff --git a/dist/signalify.js.map b/dist/signalify.js.map index b4b1c14..22c6dd2 100644 --- a/dist/signalify.js.map +++ b/dist/signalify.js.map @@ -1 +1 @@ -{"version":3,"file":"signalify.js","names":["getInheritedDescriptor","createSignal","$PROXY","untrack","signalifiedProps","WeakMap","signalify","obj","props","_props","length","Object","keys","concat","getOwnPropertySymbols","prop","createSignalAccessor","gotCreateSignalAccessor","getCreateSignalAccessor","Error","propsSetAtLeastOnce","__isPropSetAtLeastOnce","instance","get","has","trackPropSetAtLeastOnce","set","Set","add","isSignalGetter","WeakSet","initialVal","override","proxy","descriptor","originalGet","originalSet","console","warn","toString","value","writable","s","equals","configurable","enumerable","call","newValue","defineProperty"],"sources":["../src/signalify.ts"],"sourcesContent":["import {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js'\nimport {createSignal, $PROXY, untrack} from 'solid-js'\nimport type {PropKey, PropSpec} from './decorators/types.js'\n\nconst signalifiedProps = new WeakMap>()\n\n/**\n * Convert properties on an object into Solid signal-backed properties.\n *\n * There are two ways to use this: either by defining which properties to\n * convert to signal-backed properties by providing an array as property names\n * in the second arg, which is useful on plain objects, or by passing in `this`\n * and `this.constructor` within the `constructor` of a class that has\n * properties decorated with `@signal`.\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, ...props: (keyof T)[]): T\nexport function signalify(obj: T): T\nexport function signalify(obj: Obj, ...props: [] | [Map] | PropertyKey[]) {\n\t// We cast from PropertyKey[] to PropKey[] because numbers can't actually be keys, only string | symbol.\n\tconst _props = props.length\n\t\t? (props as PropKey[])\n\t\t: (Object.keys(obj) as PropKey[]).concat(Object.getOwnPropertySymbols(obj))\n\n\tfor (const prop of _props) createSignalAccessor(obj, prop)\n\n\treturn obj\n}\n\nlet gotCreateSignalAccessor = false\n\n/**\n * This ensures that `createSignalAccessor` is kept internal to classy-solid only.\n */\nexport function getCreateSignalAccessor() {\n\tif (gotCreateSignalAccessor) throw new Error('Export \"createSignalAccessor\" is internal to classy-solid only.')\n\tgotCreateSignalAccessor = true\n\treturn createSignalAccessor\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\nfunction trackPropSetAtLeastOnce(instance: object, prop: string | symbol) {\n\tif (!propsSetAtLeastOnce.has(instance)) propsSetAtLeastOnce.set(instance, new Set())\n\tpropsSetAtLeastOnce.get(instance)!.add(prop)\n}\n\nconst isSignalGetter = new WeakSet()\n\nfunction createSignalAccessor(\n\tobj: T,\n\tprop: Exclude,\n\t// Untrack here to be extra safe this doesn't count as a dependency and\n\t// cause a reactivity loop.\n\tinitialVal: unknown = untrack(() => obj[prop]),\n\t// If an object already has a particular signalified property, override it\n\t// with a new one anyway (useful for maintaining consistency with class\n\t// inheritance where class fields always override fields from base classes\n\t// due to their [[Define]] semantics). False is a good default for signalify()\n\t// usage where someone is augmenting an existing object, but true is more\n\t// useful with usage of @signal on class fields.\n\t//\n\t// Note that if @signal were to specify this as false, it would cause\n\t// @signal-decorated subclass fields to override base class\n\t// @signal-decorated fields with a new value descriptor but without\n\t// signalifiying the field, effectively disabling reactivity, which is a bug\n\t// (a field decorated with @signal *must* be reactive). The test named\n\t// \"maintains reactivity in subclass overridden fields\" was added to ensure\n\t// that the subclass use case works.\n\toverride = false,\n): void {\n\tif (!override && signalifiedProps.get(obj)?.has(prop)) return\n\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\n\n\tlet descriptor: PropertyDescriptor | undefined = getInheritedDescriptor(obj, prop)\n\n\tlet originalGet: (() => any) | undefined\n\tlet originalSet: ((v: any) => void) | undefined\n\n\tif (descriptor) {\n\t\toriginalGet = descriptor.get\n\t\toriginalSet = descriptor.set\n\n\t\t// Even if override is true, if we have a signal accessor, there's no\n\t\t// need to replace it with another signal accessor. We only need to\n\t\t// override when the current descriptor is not a signal accessor.\n\t\t// TODO this needs tests.\n\t\tif (originalGet && isSignalGetter.has(originalGet)) return\n\n\t\tif (originalGet || originalSet) {\n\t\t\t// reactivity requires both\n\t\t\tif (!originalGet || !originalSet) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`The \\`@signal\\` decorator was used on an accessor named \"${prop.toString()}\" which had a getter or a setter, but not both. Reactivity on accessors works only when accessors have both get and set. In this case the decorator does not do anything.`,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tdelete descriptor.get\n\t\t\tdelete descriptor.set\n\t\t} else {\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\n\t\t\t// if it isn't writable, we don't need to make a reactive variable because\n\t\t\t// the value won't change\n\t\t\tif (!descriptor.writable) {\n\t\t\t\tconsole.warn(\n\t\t\t\t\t`The \\`@signal\\` decorator was used on a property named \"${prop.toString()}\" that is not writable. Reactivity is not enabled for non-writable properties.`,\n\t\t\t\t)\n\t\t\t\treturn\n\t\t\t}\n\n\t\t\tdelete descriptor.value\n\t\t\tdelete descriptor.writable\n\t\t}\n\t}\n\n\tconst s = createSignal(initialVal, {equals: false})\n\n\tdescriptor = {\n\t\tconfigurable: true,\n\t\tenumerable: true,\n\t\t...descriptor,\n\t\tget: originalGet\n\t\t\t? function (this: T): unknown {\n\t\t\t\t\ts[0]() // read\n\t\t\t\t\treturn originalGet!.call(this)\n\t\t\t }\n\t\t\t: function (this: any): unknown {\n\t\t\t\t\treturn s[0]() // read\n\t\t\t },\n\t\tset: originalSet\n\t\t\t? function (this: any, newValue: unknown) {\n\t\t\t\t\toriginalSet!.call(this, newValue)\n\n\t\t\t\t\ttrackPropSetAtLeastOnce(this, prop)\n\n\t\t\t\t\t// write\n\t\t\t\t\tif (typeof newValue === 'function') s[1](() => newValue)\n\t\t\t\t\telse s[1](newValue)\n\t\t\t }\n\t\t\t: function (this: any, newValue: unknown) {\n\t\t\t\t\ttrackPropSetAtLeastOnce(this, prop)\n\n\t\t\t\t\t// write\n\t\t\t\t\tif (typeof newValue === 'function') s[1](() => newValue)\n\t\t\t\t\telse s[1](newValue)\n\t\t\t },\n\t}\n\n\tisSignalGetter.add(descriptor.get!)\n\n\tObject.defineProperty(obj, prop, descriptor)\n\n\tif (!signalifiedProps.has(obj)) signalifiedProps.set(obj, new Set())\n\tsignalifiedProps.get(obj)!.add(prop)\n}\n\ntype Obj = Record\n"],"mappings":"AAAA,SAAQA,sBAAsB,QAAO,yCAAyC;AAC9E,SAAQC,YAAY,EAAEC,MAAM,EAAEC,OAAO,QAAO,UAAU;AAGtD,MAAMC,gBAAgB,GAAG,IAAIC,OAAO,CAA+B,CAAC;;AAEpE;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;;AAGA,OAAO,SAASC,SAASA,CAACC,GAAQ,EAAE,GAAGC,KAAoD,EAAE;EAC5F;EACA,MAAMC,MAAM,GAAGD,KAAK,CAACE,MAAM,GACvBF,KAAK,GACLG,MAAM,CAACC,IAAI,CAACL,GAAG,CAAC,CAAeM,MAAM,CAACF,MAAM,CAACG,qBAAqB,CAACP,GAAG,CAAC,CAAC;EAE5E,KAAK,MAAMQ,IAAI,IAAIN,MAAM,EAAEO,oBAAoB,CAACT,GAAG,EAAEQ,IAAI,CAAC;EAE1D,OAAOR,GAAG;AACX;AAEA,IAAIU,uBAAuB,GAAG,KAAK;;AAEnC;AACA;AACA;AACA,OAAO,SAASC,uBAAuBA,CAAA,EAAG;EACzC,IAAID,uBAAuB,EAAE,MAAM,IAAIE,KAAK,CAAC,iEAAiE,CAAC;EAC/GF,uBAAuB,GAAG,IAAI;EAC9B,OAAOD,oBAAoB;AAC5B;;AAEA;AACA;AACA,MAAMI,mBAAmB,GAAG,IAAIf,OAAO,CAA+B,CAAC;;AAEvE;AACA;AACA;AACA,OAAO,SAASgB,sBAAsBA,CAACC,QAAgB,EAAEP,IAAqB,EAAE;EAC/E,OAAO,CAAC,CAACK,mBAAmB,CAACG,GAAG,CAACD,QAAQ,CAAC,EAAEE,GAAG,CAACT,IAAI,CAAC;AACtD;AAEA,SAASU,uBAAuBA,CAACH,QAAgB,EAAEP,IAAqB,EAAE;EACzE,IAAI,CAACK,mBAAmB,CAACI,GAAG,CAACF,QAAQ,CAAC,EAAEF,mBAAmB,CAACM,GAAG,CAACJ,QAAQ,EAAE,IAAIK,GAAG,CAAC,CAAC,CAAC;EACpFP,mBAAmB,CAACG,GAAG,CAACD,QAAQ,CAAC,CAAEM,GAAG,CAACb,IAAI,CAAC;AAC7C;AAEA,MAAMc,cAAc,GAAG,IAAIC,OAAO,CAAW,CAAC;AAE9C,SAASd,oBAAoBA,CAC5BT,GAAM,EACNQ,IAA8B;AAC9B;AACA;AACAgB,UAAmB,GAAG5B,OAAO,CAAC,MAAMI,GAAG,CAACQ,IAAI,CAAC,CAAC;AAC9C;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACA;AACAiB,QAAQ,GAAG,KAAK,EACT;EACP,IAAI,CAACA,QAAQ,IAAI5B,gBAAgB,CAACmB,GAAG,CAAChB,GAAG,CAAC,EAAEiB,GAAG,CAACT,IAAI,CAAC,EAAE;;EAEvD;EACA;EACA;EACA,MAAMkB,KAAK,GAAG1B,GAAG,CAACL,MAAM,CAAM;EAC9B,IAAI+B,KAAK,EAAE;EAEX,IAAIC,UAA0C,GAAGlC,sBAAsB,CAACO,GAAG,EAAEQ,IAAI,CAAC;EAElF,IAAIoB,WAAoC;EACxC,IAAIC,WAA2C;EAE/C,IAAIF,UAAU,EAAE;IACfC,WAAW,GAAGD,UAAU,CAACX,GAAG;IAC5Ba,WAAW,GAAGF,UAAU,CAACR,GAAG;;IAE5B;IACA;IACA;IACA;IACA,IAAIS,WAAW,IAAIN,cAAc,CAACL,GAAG,CAACW,WAAW,CAAC,EAAE;IAEpD,IAAIA,WAAW,IAAIC,WAAW,EAAE;MAC/B;MACA,IAAI,CAACD,WAAW,IAAI,CAACC,WAAW,EAAE;QACjCC,OAAO,CAACC,IAAI,CACX,4DAA4DvB,IAAI,CAACwB,QAAQ,CAAC,CAAC,2KAC5E,CAAC;QACD;MACD;MAEA,OAAOL,UAAU,CAACX,GAAG;MACrB,OAAOW,UAAU,CAACR,GAAG;IACtB,CAAC,MAAM;MACN;MACA;MACA;MACA;MACAK,UAAU,GAAGG,UAAU,CAACM,KAAK;;MAE7B;MACA;MACA,IAAI,CAACN,UAAU,CAACO,QAAQ,EAAE;QACzBJ,OAAO,CAACC,IAAI,CACX,2DAA2DvB,IAAI,CAACwB,QAAQ,CAAC,CAAC,gFAC3E,CAAC;QACD;MACD;MAEA,OAAOL,UAAU,CAACM,KAAK;MACvB,OAAON,UAAU,CAACO,QAAQ;IAC3B;EACD;EAEA,MAAMC,CAAC,GAAGzC,YAAY,CAAC8B,UAAU,EAAE;IAACY,MAAM,EAAE;EAAK,CAAC,CAAC;EAEnDT,UAAU,GAAG;IACZU,YAAY,EAAE,IAAI;IAClBC,UAAU,EAAE,IAAI;IAChB,GAAGX,UAAU;IACbX,GAAG,EAAEY,WAAW,GACb,YAA4B;MAC5BO,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAC;MACP,OAAOP,WAAW,CAAEW,IAAI,CAAC,IAAI,CAAC;IAC9B,CAAC,GACD,YAA8B;MAC9B,OAAOJ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,EAAC;IACd,CAAC;IACJhB,GAAG,EAAEU,WAAW,GACb,UAAqBW,QAAiB,EAAE;MACxCX,WAAW,CAAEU,IAAI,CAAC,IAAI,EAAEC,QAAQ,CAAC;MAEjCtB,uBAAuB,CAAC,IAAI,EAAEV,IAAI,CAAC;;MAEnC;MACA,IAAI,OAAOgC,QAAQ,KAAK,UAAU,EAAEL,CAAC,CAAC,CAAC,CAAC,CAAC,MAAMK,QAAQ,CAAC,MACnDL,CAAC,CAAC,CAAC,CAAC,CAACK,QAAQ,CAAC;IACnB,CAAC,GACD,UAAqBA,QAAiB,EAAE;MACxCtB,uBAAuB,CAAC,IAAI,EAAEV,IAAI,CAAC;;MAEnC;MACA,IAAI,OAAOgC,QAAQ,KAAK,UAAU,EAAEL,CAAC,CAAC,CAAC,CAAC,CAAC,MAAMK,QAAQ,CAAC,MACnDL,CAAC,CAAC,CAAC,CAAC,CAACK,QAAQ,CAAC;IACnB;EACJ,CAAC;EAEDlB,cAAc,CAACD,GAAG,CAACM,UAAU,CAACX,GAAI,CAAC;EAEnCZ,MAAM,CAACqC,cAAc,CAACzC,GAAG,EAAEQ,IAAI,EAAEmB,UAAU,CAAC;EAE5C,IAAI,CAAC9B,gBAAgB,CAACoB,GAAG,CAACjB,GAAG,CAAC,EAAEH,gBAAgB,CAACsB,GAAG,CAACnB,GAAG,EAAE,IAAIoB,GAAG,CAAC,CAAC,CAAC;EACpEvB,gBAAgB,CAACmB,GAAG,CAAChB,GAAG,CAAC,CAAEqB,GAAG,CAACb,IAAI,CAAC;AACrC","ignoreList":[]} \ No newline at end of file +{"version":3,"file":"signalify.js","names":["getInheritedDescriptor","createSignal","$PROXY","untrack","signalify","obj","props","proxy","_props","length","Object","keys","concat","getOwnPropertySymbols","prop","isTuple","Array","isArray","_prop","initialValue","createSignalAccessor","gotCreateSignalAccessor","getCreateSignalAccessor","Error","propsSetAtLeastOnce","WeakMap","__isPropSetAtLeastOnce","instance","get","has","trackPropSetAtLeastOnce","set","Set","add","isSignalGetter","WeakSet","initialVal","descriptor","originalGet","originalSet","warnNotReadWrite","value","writable","warnNotWritable","signalStorage","configurable","enumerable","s","getSignal","call","newValue","defineProperty","storage","equals","console","warn","String"],"sources":["../src/signalify.ts"],"sourcesContent":["import {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js'\nimport {createSignal, $PROXY, untrack, type Signal} from 'solid-js'\nimport type {PropKey} from './decorators/types.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: either by defining which properties to\n * convert to signal-backed properties by providing an array as property names\n * in the second arg, which is useful on plain objects, or by passing in `this`\n * and `this.constructor` within the `constructor` of a class that has\n * properties decorated with `@signal`.\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\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 _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)\n\t}\n\n\treturn obj\n}\n\nlet gotCreateSignalAccessor = false\n\n/**\n * This ensures that `createSignalAccessor` is kept internal to classy-solid only.\n */\nexport function getCreateSignalAccessor() {\n\tif (gotCreateSignalAccessor) throw new Error('Export \"createSignalAccessor\" is internal to classy-solid only.')\n\tgotCreateSignalAccessor = true\n\treturn createSignalAccessor\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\nfunction trackPropSetAtLeastOnce(instance: object, prop: string | symbol) {\n\tif (!propsSetAtLeastOnce.has(instance)) propsSetAtLeastOnce.set(instance, new Set())\n\tpropsSetAtLeastOnce.get(instance)!.add(prop)\n}\n\nconst isSignalGetter = new WeakSet()\n\nfunction createSignalAccessor(obj: T, prop: Exclude, initialVal: unknown): void {\n\tlet descriptor: PropertyDescriptor | undefined = getInheritedDescriptor(obj, prop)\n\n\tlet originalGet: (() => any) | undefined\n\tlet originalSet: ((v: any) => void) | undefined\n\n\tif (descriptor) {\n\t\toriginalGet = descriptor.get\n\t\toriginalSet = descriptor.set\n\n\t\tif (originalGet && isSignalGetter.has(originalGet)) return\n\n\t\tif (originalGet || originalSet) {\n\t\t\t// reactivity requires both\n\t\t\tif (!originalGet || !originalSet) return warnNotReadWrite(prop)\n\n\t\t\tdelete descriptor.get\n\t\t\tdelete descriptor.set\n\t\t} else {\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\n\t\t\t// if it isn't writable, we don't need to make a reactive variable because\n\t\t\t// the value won't change\n\t\t\tif (!descriptor.writable) return warnNotWritable(prop)\n\n\t\t\tdelete descriptor.value\n\t\t\tdelete descriptor.writable\n\t\t}\n\t}\n\n\tconst signalStorage = new WeakMap>()\n\n\tdescriptor = {\n\t\tconfigurable: true,\n\t\tenumerable: true,\n\t\t...descriptor,\n\t\tget: originalGet\n\t\t\t? function (this: object): unknown {\n\t\t\t\t\tconst s = getSignal(this, signalStorage, initialVal)\n\t\t\t\t\ts[0]() // read\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\tconst s = getSignal(this, signalStorage, initialVal)\n\t\t\t\t\treturn s[0]() // read\n\t\t\t },\n\t\tset: originalSet\n\t\t\t? function (this: object, newValue: unknown) {\n\t\t\t\t\toriginalSet!.call(this, newValue)\n\n\t\t\t\t\ttrackPropSetAtLeastOnce(this, prop)\n\n\t\t\t\t\t// write\n\t\t\t\t\tconst s = getSignal(this, signalStorage, initialVal)\n\t\t\t\t\tif (typeof newValue === 'function') s[1](() => newValue)\n\t\t\t\t\telse s[1](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\t// write\n\t\t\t\t\tconst s = getSignal(this, signalStorage, initialVal)\n\t\t\t\t\tif (typeof newValue === 'function') s[1](() => newValue)\n\t\t\t\t\telse s[1](newValue)\n\t\t\t },\n\t}\n\n\tisSignalGetter.add(descriptor.get!)\n\n\tObject.defineProperty(obj, prop, descriptor)\n}\n\nfunction getSignal(obj: object, storage: WeakMap>, initialVal: unknown) {\n\tlet s = storage.get(obj)\n\tif (!s) storage.set(obj, (s = createSignal(initialVal, {equals: false})))\n\treturn s\n}\n\nfunction warnNotReadWrite(prop: PropertyKey) {\n\tconsole.warn(\n\t\t`Cannot signalify property named \"${String(\n\t\t\tprop,\n\t\t)}\" which had a getter or a setter, but not both. Reactivity on accessors works only when accessors have both get and set. Skipped.`,\n\t)\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/src/decorators/reactive.ts b/src/decorators/reactive.ts index e0e9210..d0853f8 100644 --- a/src/decorators/reactive.ts +++ b/src/decorators/reactive.ts @@ -1,5 +1,5 @@ import type {AnyConstructor} from 'lowclass/dist/Constructor.js' -import {getListener, untrack} from 'solid-js' +import {getListener, $PROXY, untrack} from 'solid-js' import {getKey, getPropsToSignalify, resetPropsToSignalify} from './signal.js' import {getCreateSignalAccessor} from '../signalify.js' @@ -66,22 +66,33 @@ export function reactive(value: AnyConstructor, context: ClassDecoratorContext | if (getListener()) untrack(() => (instance = Reflect.construct(Class, args, new.target))) // super() else super(...args), (instance = this) - for (const [prop, {initialValue}] of signalProps) { + // 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 = instance[$PROXY] as T + if (proxy) return instance + + for (const [prop, propSpec] of signalProps) { + const kind = propSpec.kind + let initialValue = propSpec.initialValue + // @prod-prune - if (!(hasOwnProperty.call(instance, prop) || hasOwnProperty.call(Class.prototype, prop))) { - throw new Error( - `Property "${prop.toString()}" not found on instance of class decorated with \`@reactive\`. Did you forget to use the \`@reactive\` decorator on one of your classes that has a "${prop.toString()}" property decorated with \`@signal\`?`, - ) - } + if (!(hasOwnProperty.call(instance, prop) || hasOwnProperty.call(Class.prototype, prop))) + throw new PropNotFoundError(prop) + + const isAccessor = kind === 'getter' || kind === 'setter' - // For now at least, we always override like class fields with - // [[Define]] semantics. Perhaps when @signal is used on a - // getter/setter, we should not override in that case, but patch - // the prototype getter/setter (that'll be a bit of work to - // implement though). - const override = true + if (isAccessor) { + const desc = Object.getOwnPropertyDescriptor(Class.prototype, prop)! + initialValue = desc.get!.call(instance) + // Note, if the kind was field, then the initializer already defined the initialValue. + } - createSignalAccessor(instance, prop as Exclude, initialValue, override) + createSignalAccessor( + isAccessor ? Class.prototype : instance, + prop as Exclude, + initialValue, + ) } return instance @@ -90,3 +101,15 @@ export function reactive(value: AnyConstructor, context: ClassDecoratorContext | return ReactiveDecorator } + +class PropNotFoundError extends Error { + constructor(prop: PropertyKey) { + super( + `Property "${String( + prop, + )}" not found on instance of class decorated with \`@reactive\`. Did you forget to use the \`@reactive\` decorator on one of your classes that has a "${String( + prop, + )}" property decorated with \`@signal\`?`, + ) + } +} diff --git a/src/decorators/signal.ts b/src/decorators/signal.ts index 16b28f4..aa351a2 100644 --- a/src/decorators/signal.ts +++ b/src/decorators/signal.ts @@ -81,13 +81,13 @@ export function signal( } if (kind === 'field') { - props.set(name, {initialValue: undefined}) + props.set(name, {initialValue: undefined, kind}) return function (this: object, initialValue: unknown) { props.get(name)!.initialValue = initialValue return initialValue } } else if (kind === 'getter' || kind === 'setter') { - props.set(name, {initialValue: undefined}) + props.set(name, {initialValue: undefined, kind}) } else { throw new Error( 'The @signal decorator is only for use on fields, getters, and setters. Auto accessor support is coming next if there is demand for it.', diff --git a/src/decorators/types.ts b/src/decorators/types.ts index b62f86c..1e2130b 100644 --- a/src/decorators/types.ts +++ b/src/decorators/types.ts @@ -4,8 +4,11 @@ export type DecoratedValue = Constructor | Function | ClassAccessorDecoratorTarg export type PropKey = string | symbol +export type SupportedKind = 'field' | 'getter' | 'setter' + // If we add options for `@signal` later (f.e. `@signal({equals: false})`), // those options can go in here too. export interface PropSpec { initialValue: unknown + kind: SupportedKind } diff --git a/src/index.test.ts b/src/index.test.ts index 2d5532b..121839e 100644 --- a/src/index.test.ts +++ b/src/index.test.ts @@ -132,52 +132,79 @@ describe('classy-solid', () => { }) }) - describe('@reactive, @signal, and signalify', () => { - it('makes class properties reactive, using class and property/accessor decorators', () => { - @reactive - class Butterfly { - @signal colors = 3 - _wingSize = 2 + describe('@reactive, @signal', () => { + @reactive + class Butterfly { + @signal colors = 3 - @signal - get wingSize() { - return this._wingSize - } - set wingSize(s: number) { - this._wingSize = s - } + _wingSize = 2 + + @signal + get wingSize() { + return this._wingSize } + set wingSize(s: number) { + this._wingSize = s + } + } + it('makes class fields reactive, using class and field/accessor decorators', () => { const b = new Butterfly() testButterflyProps(b) }) - it('maintains reactivity in subclass overridden fields', async () => { - @reactive - class Butterfly { - @signal colors = 3 - _wingSize = 2 + const ensure = it - @signal - get wingSize() { - return this._wingSize - } - set wingSize(s: number) { - this._wingSize = s - } + ensure('overridden fields work as expected', async () => { + class Mid extends Butterfly { + override colors = 0 } + // ensure subclass did not interfere with functionality of base class + const b0 = new Butterfly() + testProp(b0, 'colors', 3, 4, true) + expect(Object.getOwnPropertyDescriptor(b0, 'colors')?.get?.call(b0) === 4).toBe(true) // accessor descriptor + @reactive - class SubButterfly extends Butterfly { + class SubButterfly extends Mid { @signal override colors = 123 } - const b = new SubButterfly() + // ensure subclass did not interfere with functionality of base class + const m = new Mid() + testProp(m, 'colors', 0, 1, false) + expect(Object.getOwnPropertyDescriptor(m, 'colors')?.value === 1).toBe(true) // value descriptor + + class SubSubButterfly extends SubButterfly { + override colors = 456 + } + const b = new SubButterfly() testButterflyProps(b, 123) + + const b2 = new SubSubButterfly() + + testProp(b2, 'colors', 456, 654, false) }) + function testProp(o: T, k: keyof T, startVal: any, newVal: any, reactive = true) { + let count = 0 + + createEffect(() => { + o[k] + count++ + }) + + expect(o[k]).toBe(startVal) + expect(count).toBe(1) + + o[k] = newVal // should not be a signal, should not trigger + + expect(o[k]).toBe(newVal) + expect(count).toBe(reactive ? 2 : 1) + } + it('does not prevent superclass constructor from receiving subclass constructor args', () => { @reactive class Insect { @@ -187,6 +214,7 @@ describe('classy-solid', () => { @reactive class Butterfly extends Insect { @signal colors = 3 + _wingSize = 2 @signal @@ -208,157 +236,6 @@ describe('classy-solid', () => { testButterflyProps(b) }) - it('makes class properties reactive, not using any decorators, specified in the constructor', () => { - class Butterfly { - colors = 3 - _wingSize = 2 - - get wingSize() { - return this._wingSize - } - set wingSize(s: number) { - this._wingSize = s - } - - constructor() { - signalify(this, 'colors', 'wingSize') - } - } - - const b = new Butterfly() - - testButterflyProps(b) - - // quick type check: - const b2 = new Butterfly() - signalify( - b2, - 'colors', - 'wingSize', - // @ts-expect-error "foo" is not a property on Butterfly - 'foo', - ) - }) - - it('makes class properties reactive, with signalify in the constructor', () => { - class Butterfly { - colors: number - _wingSize: number - - get wingSize() { - return this._wingSize - } - set wingSize(s: number) { - this._wingSize = s - } - - constructor() { - this.colors = 3 - this._wingSize = 2 - - signalify(this, 'colors', 'wingSize') - } - } - - const b = new Butterfly() - - testButterflyProps(b) - }) - - it('works with a function-style class, with signalify in the constructor', () => { - function Butterfly() { - // @ts-ignore - this.colors = 3 - // @ts-ignore - this._wingSize = 2 - - // @ts-ignore no type checking for ES5-style classes. - signalify(this, 'colors', 'wingSize') - } - - Butterfly.prototype = { - get wingSize() { - return this._wingSize - }, - set wingSize(s: number) { - this._wingSize = s - }, - } - - // @ts-ignore - const b = new Butterfly() - testButterflyProps(b) - }) - - it('works with a function-style class, with properties on the prototype, and signalify in constructor', () => { - function Butterfly() { - // @ts-ignore no type checking for ES5-style classes. - signalify(this, 'colors', 'wingSize') - } - - Butterfly.prototype = { - colors: 3, - _wingSize: 2, - - get wingSize() { - return this._wingSize - }, - set wingSize(s: number) { - this._wingSize = s - }, - } - - // @ts-ignore no type checking for ES5-style classes. - const b = new Butterfly() - testButterflyProps(b) - }) - - it('can be used on a function-style class, with properties on the prototype, and signalify on the prototype', () => { - function Butterfly() {} - - Butterfly.prototype = { - colors: 3, - _wingSize: 2, - - get wingSize() { - return this._wingSize - }, - set wingSize(s: number) { - this._wingSize = s - }, - } - - signalify(Butterfly.prototype, 'colors', 'wingSize') - - // @ts-ignore no type checking for ES5-style classes. - const b = new Butterfly() - testButterflyProps(b) - }) - - it('can be used on a function-style class, with properties in the constructor, and signalify on the prototype', () => { - function Butterfly() { - // @ts-ignore - this.colors = 3 - // @ts-ignore - this._wingSize = 2 - } - - Butterfly.prototype = { - get wingSize() { - return this._wingSize - }, - set wingSize(s: number) { - this._wingSize = s - }, - } - - signalify(Butterfly.prototype, 'colors', 'wingSize') - - // @ts-ignore - const b = new Butterfly() - testButterflyProps(b) - }) - it('throws an error when @signal is used without @reactive', async () => { expect(() => { // user forgot to use @reactive here @@ -425,137 +302,370 @@ describe('classy-solid', () => { expect(doer.do()).toBe(123) }) - describe('signalify', () => { - it('is not tracked inside of an effect to prevent loops', () => { - // Library author provides obj - const obj = {n: 123} - signalify(obj, 'n') // library author might signalify obj.n + it('automatically does not track reactivity in constructors when using decorators', () => { + @reactive + class Foo { + @signal amount = 3 + } - // User code: + @reactive + class Bar extends Foo { + @signal double = 0 + + constructor() { + super() + this.double = this.amount * 2 // this read of .amount should not be tracked + } + } + + let b: Bar + let count = 0 + + function noLoop() { createEffect(() => { - // 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. - signalify(obj, 'n') + b = new Bar() // this should not track + count++ + }) + } + + expect(noLoop).not.toThrow() + + const b2 = b! + + b!.amount = 4 // hence this should not trigger + + // If the effect ran only once initially, not when setting b.colors, + // then both variables should reference the same instance + expect(count).toBe(1) + expect(b!).toBe(b2) + }) + }) + + describe('signalify()', () => { + it('returns the same object that was passed in', () => { + let obj = {n: 123} + let obj2 = signalify(obj, 'n') + expect(obj).toBe(obj2) - obj.n = 123 // does not make an infinite loop + obj = createMutable({n: 123}) + obj2 = signalify(obj, 'n') + expect(obj).toBe(obj2) + }) + + describe('making objects reactive with signalify()', () => { + it('', () => { + const butterfly = { + colors: 3, + + _wingSize: 2, + + get wingSize() { + return this._wingSize + }, + set wingSize(s: number) { + this._wingSize = s + }, + } - // A deeper effect will be reading the property. + const b = signalify(butterfly, 'colors', 'wingSize') + + testButterflyProps(b) + + // quick type check: + // @ts-expect-error "foo" is not a property on butterfly + signalify(butterfly, 'colors', 'wingSize', 'foo') + }) + + it('is not tracked inside of an effect to prevent loops', () => { + test(true) + test(false) + + function test(signalifyInitially: boolean) { + // Library author provides obj + const obj = {n: 123} + if (signalifyInitially) signalify(obj, 'n') // library author might signalify obj.n + + // User code: createEffect(() => { - console.log(obj.n) + // 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. + signalify(obj, 'n') + + obj.n = 123 // does not make an infinite loop + + // A deeper effect will be reading the property. + createEffect(() => { + console.log(obj.n) + }) }) - }) - // No expectations in this test, the test passes if a maximum - // callstack size error (infinite loop) does not happen. + // No expectations in this test, the test passes if a maximum + // callstack size error (infinite loop) does not happen. + } }) }) - it('show that signalify causes constructor to be reactive when used manually instead of decorators', () => { - class Foo { - amount = 3 + describe('making reactive classes with signalify instead of with decorators', () => { + it('makes class fields reactive, not using any decorators', () => { + class Butterfly { + colors = 3 - constructor() { - signalify(this, 'amount') + _wingSize = 2 + + get wingSize() { + return this._wingSize + } + set wingSize(s: number) { + this._wingSize = s + } + + constructor() { + signalify(this, 'colors', 'wingSize') + } } - } - class Bar extends Foo { - double = 0 + const b = new Butterfly() - constructor() { - super() - signalify(this, 'double') - this.double = this.amount * 2 // this tracks access of .amount + testButterflyProps(b) + + // quick type check: + const b2 = new Butterfly() + // @ts-expect-error "foo" is not a property on Butterfly + signalify(b2, 'colors', 'wingSize', 'foo') + }) + + it('makes constructor properties reactive, not using any decorators', () => { + class Butterfly { + declare colors: number + + declare _wingSize: number + + get wingSize() { + return this._wingSize + } + set wingSize(s: number) { + this._wingSize = s + } + + constructor() { + this.colors = 3 + this._wingSize = 2 + + signalify(this, 'colors', 'wingSize') + } } - } - let count = 0 - let b!: Bar + const b = new Butterfly() - createEffect(() => { - b = new Bar() // tracks .amount - count++ + testButterflyProps(b) }) - expect(count).toBe(1) + it('works with a function-style class, with signalify in the constructor', () => { + function Butterfly() { + // @ts-ignore + this.colors = 3 + // @ts-ignore + this._wingSize = 2 - b.amount = 4 // triggers + // @ts-ignore no type checking for ES5-style classes. + signalify(this, 'colors', 'wingSize') + } - expect(count).toBe(2) - }) + Butterfly.prototype = { + get wingSize() { + return this._wingSize + }, + set wingSize(s: number) { + this._wingSize = s + }, + } - it('show how to manually untrack constructors when not using decorators', () => { - class Foo { - amount = 3 + // @ts-ignore + const b = new Butterfly() + testButterflyProps(b) + }) - constructor() { - signalify(this, 'amount') + it('works with a function-style class, with properties on the prototype, and signalify in constructor', () => { + function Butterfly() { + // @ts-ignore no type checking for ES5-style classes. + signalify(this, 'colors', 'wingSize') } - } - class Bar extends Foo { - double = 0 + Butterfly.prototype = { + colors: 3, + _wingSize: 2, - constructor() { - super() - signalify(this, 'double') + get wingSize() { + return this._wingSize + }, + set wingSize(s: number) { + this._wingSize = s + }, + } - untrack(() => { - this.double = this.amount * 2 - }) + // @ts-ignore no type checking for ES5-style classes. + const b = new Butterfly() + testButterflyProps(b) + }) + + it('can be used on a function-style class, with properties on the prototype, and signalify on the prototype', () => { + function Butterfly() {} + + Butterfly.prototype = { + colors: 3, + _wingSize: 2, + + get wingSize() { + return this._wingSize + }, + set wingSize(s: number) { + this._wingSize = s + }, } - } - let count = 0 - let b!: Bar + signalify(Butterfly.prototype, 'colors', 'wingSize') - createEffect(() => { - b = new Bar() // does not track .amount - count++ + // @ts-ignore no type checking for ES5-style classes. + const b = new Butterfly() + testButterflyProps(b) }) - expect(count).toBe(1) + it('can be used on a function-style class, with properties in the constructor, and signalify on the prototype', () => { + function Butterfly() { + // @ts-ignore + this.colors = 3 + // @ts-ignore + this._wingSize = 2 + } - b.amount = 4 // will not trigger + Butterfly.prototype = { + get wingSize() { + return this._wingSize + }, + set wingSize(s: number) { + this._wingSize = s + }, + } - expect(count).toBe(1) - }) + signalify(Butterfly.prototype, 'colors', 'wingSize') - it('automatically does not track reactivity in constructors when using decorators', () => { - @reactive - class Foo { - @signal amount = 3 - } + // @ts-ignore + const b = new Butterfly() + testButterflyProps(b) + }) - @reactive - class Bar extends Foo { - @signal double = 0 + it('show that signalify causes constructor to be reactive when used manually instead of decorators', () => { + class Foo { + amount = 3 - constructor() { - super() - this.double = this.amount * 2 // this read of .amount should not be tracked + constructor() { + signalify(this, 'amount') + } } - } - let b: Bar - let count = 0 + class Bar extends Foo { + double = 0 + + constructor() { + super() + signalify(this, 'double') + this.double = this.amount * 2 // this tracks access of .amount + } + } + + let count = 0 + let b!: Bar - function noLoop() { createEffect(() => { - b = new Bar() // this should not track + b = new Bar() // tracks .amount count++ }) - } - expect(noLoop).not.toThrow() + expect(count).toBe(1) - const b2 = b! + b.amount = 4 // triggers - b!.amount = 4 // hence this should not trigger + expect(count).toBe(2) + }) - // If the effect ran only once initially, not when setting b.colors, - // then both variables should reference the same instance - expect(count).toBe(1) - expect(b!).toBe(b2) + it('show how to manually untrack constructors when not using decorators', () => { + class Foo { + amount = 3 + + constructor() { + signalify(this, 'amount') + } + } + + class Bar extends Foo { + double = 0 + + constructor() { + super() + signalify(this, 'double') + + untrack(() => { + this.double = this.amount * 2 + }) + } + } + + let count = 0 + let b!: Bar + + createEffect(() => { + b = new Bar() // does not track .amount + count++ + }) + + expect(count).toBe(1) + + b.amount = 4 // will not trigger + + expect(count).toBe(1) + }) + }) + + it('creates signal storage per descriptor+object pair, not per descriptor', () => { + // This ensures we don't accidentally share a signal with multiple + // objects. For example, we don't want a single signal per descriptor + // because if the descriptor is on a prototype object, then that single + // signal will erroneously be used by all objects extending from that + // prototype. + + const a = signalify({foo: 0, name: 'a'}, 'foo') + const b = Object.assign(Object.create(a) as typeof a, {name: 'b'}) + + expect(a.foo).toBe(0) + expect(b.foo).toBe(0) + + let countA = 0 + + createEffect(() => { + a.foo + countA++ + }) + + let countB = 0 + + createEffect(() => { + b.foo + countB++ + }) + + expect(countA).toBe(1) + expect(countB).toBe(1) + + a.foo++ + + expect(a.foo).toBe(1) + expect(countA).toBe(2) + + // ensure that updating a's foo property did not update b's foo + // property or trigger b's effect, despite that the property is + // defined in a single location on the prototype. + // @ts-ignore + expect(b.foo).toBe(0) + expect(countB).toBe(1) }) }) diff --git a/src/signalify.ts b/src/signalify.ts index 03497fa..fe76078 100644 --- a/src/signalify.ts +++ b/src/signalify.ts @@ -1,8 +1,8 @@ import {getInheritedDescriptor} from 'lowclass/dist/getInheritedDescriptor.js' -import {createSignal, $PROXY, untrack} from 'solid-js' -import type {PropKey, PropSpec} from './decorators/types.js' +import {createSignal, $PROXY, untrack, type Signal} from 'solid-js' +import type {PropKey} from './decorators/types.js' -const signalifiedProps = new WeakMap>() +type AnyObject = Record /** * Convert properties on an object into Solid signal-backed properties. @@ -53,15 +53,28 @@ const signalifiedProps = new WeakMap>() * }) * ``` */ -export function signalify(obj: T, ...props: (keyof T)[]): T export function signalify(obj: T): T -export function signalify(obj: Obj, ...props: [] | [Map] | PropertyKey[]) { - // We cast from PropertyKey[] to PropKey[] because numbers can't actually be keys, only string | symbol. - const _props = props.length - ? (props as PropKey[]) - : (Object.keys(obj) as PropKey[]).concat(Object.getOwnPropertySymbols(obj)) +export function signalify(obj: T, ...props: (keyof T)[]): T +export function signalify(obj: T, ...props: [key: keyof T, initialValue: unknown][]): T +export function signalify(obj: AnyObject, ...props: [key: PropertyKey, initialValue: unknown][] | PropertyKey[]) { + // 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 = obj[$PROXY] as T + if (proxy) return obj + + const _props = props.length ? props : (Object.keys(obj) as PropKey[]).concat(Object.getOwnPropertySymbols(obj)) - for (const prop of _props) createSignalAccessor(obj, prop) + // Use `untrack` here to be extra safe the initial value doesn't count as a + // dependency and cause a reactivity loop. + for (const prop of _props) { + const isTuple = Array.isArray(prop) + // We cast from PropertyKey to PropKey because keys can't actually be number, only string | symbol. + const _prop = (isTuple ? prop[0] : prop) as PropKey + const initialValue = isTuple ? prop[1] : untrack(() => obj[_prop]) + + createSignalAccessor(obj, _prop, initialValue) + } return obj } @@ -95,36 +108,7 @@ function trackPropSetAtLeastOnce(instance: object, prop: string | symbol) { const isSignalGetter = new WeakSet() -function createSignalAccessor( - obj: T, - prop: Exclude, - // Untrack here to be extra safe this doesn't count as a dependency and - // cause a reactivity loop. - initialVal: unknown = untrack(() => obj[prop]), - // If an object already has a particular signalified property, override it - // with a new one anyway (useful for maintaining consistency with class - // inheritance where class fields always override fields from base classes - // due to their [[Define]] semantics). False is a good default for signalify() - // usage where someone is augmenting an existing object, but true is more - // useful with usage of @signal on class fields. - // - // Note that if @signal were to specify this as false, it would cause - // @signal-decorated subclass fields to override base class - // @signal-decorated fields with a new value descriptor but without - // signalifiying the field, effectively disabling reactivity, which is a bug - // (a field decorated with @signal *must* be reactive). The test named - // "maintains reactivity in subclass overridden fields" was added to ensure - // that the subclass use case works. - override = false, -): void { - if (!override && signalifiedProps.get(obj)?.has(prop)) return - - // 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 = obj[$PROXY] as T - if (proxy) return - +function createSignalAccessor(obj: T, prop: Exclude, initialVal: unknown): void { let descriptor: PropertyDescriptor | undefined = getInheritedDescriptor(obj, prop) let originalGet: (() => any) | undefined @@ -134,20 +118,11 @@ function createSignalAccessor( originalGet = descriptor.get originalSet = descriptor.set - // Even if override is true, if we have a signal accessor, there's no - // need to replace it with another signal accessor. We only need to - // override when the current descriptor is not a signal accessor. - // TODO this needs tests. if (originalGet && isSignalGetter.has(originalGet)) return if (originalGet || originalSet) { // reactivity requires both - if (!originalGet || !originalSet) { - console.warn( - `The \`@signal\` decorator was used on an accessor named "${prop.toString()}" which had a getter or a setter, but not both. Reactivity on accessors works only when accessors have both get and set. In this case the decorator does not do anything.`, - ) - return - } + if (!originalGet || !originalSet) return warnNotReadWrite(prop) delete descriptor.get delete descriptor.set @@ -160,46 +135,45 @@ function createSignalAccessor( // if it isn't writable, we don't need to make a reactive variable because // the value won't change - if (!descriptor.writable) { - console.warn( - `The \`@signal\` decorator was used on a property named "${prop.toString()}" that is not writable. Reactivity is not enabled for non-writable properties.`, - ) - return - } + if (!descriptor.writable) return warnNotWritable(prop) delete descriptor.value delete descriptor.writable } } - const s = createSignal(initialVal, {equals: false}) + const signalStorage = new WeakMap>() descriptor = { configurable: true, enumerable: true, ...descriptor, get: originalGet - ? function (this: T): unknown { + ? function (this: object): unknown { + const s = getSignal(this, signalStorage, initialVal) s[0]() // read return originalGet!.call(this) } - : function (this: any): unknown { + : function (this: object): unknown { + const s = getSignal(this, signalStorage, initialVal) return s[0]() // read }, set: originalSet - ? function (this: any, newValue: unknown) { + ? function (this: object, newValue: unknown) { originalSet!.call(this, newValue) trackPropSetAtLeastOnce(this, prop) // write + const s = getSignal(this, signalStorage, initialVal) if (typeof newValue === 'function') s[1](() => newValue) else s[1](newValue) } - : function (this: any, newValue: unknown) { + : function (this: object, newValue: unknown) { trackPropSetAtLeastOnce(this, prop) // write + const s = getSignal(this, signalStorage, initialVal) if (typeof newValue === 'function') s[1](() => newValue) else s[1](newValue) }, @@ -208,9 +182,26 @@ function createSignalAccessor( isSignalGetter.add(descriptor.get!) Object.defineProperty(obj, prop, descriptor) +} + +function getSignal(obj: object, storage: WeakMap>, initialVal: unknown) { + let s = storage.get(obj) + if (!s) storage.set(obj, (s = createSignal(initialVal, {equals: false}))) + return s +} - if (!signalifiedProps.has(obj)) signalifiedProps.set(obj, new Set()) - signalifiedProps.get(obj)!.add(prop) +function warnNotReadWrite(prop: PropertyKey) { + console.warn( + `Cannot signalify property named "${String( + prop, + )}" which had a getter or a setter, but not both. Reactivity on accessors works only when accessors have both get and set. Skipped.`, + ) } -type Obj = Record +function warnNotWritable(prop: PropertyKey) { + console.warn( + `The \`@signal\` decorator was used on a property named "${String( + prop, + )}" that is not writable. Reactivity is not enabled for non-writable properties.`, + ) +}