@reactively/decorate
Advanced tools
Comparing version 0.0.2 to 0.0.3
@@ -1,17 +0,38 @@ | ||
/** Collection of properties to transform into reactive properties, indexed by class. | ||
import { Reactive, ReactivelyParams } from "@reactively/core"; | ||
/** Decorate a `@reactively` property in a class. | ||
* | ||
* In the map, the key is the constructor function for a class, and the value is | ||
* a list of property names. | ||
* The decorated property can be a value, a method, or a get accessor. | ||
* The class must inherit from HasReactive (or ReactiveLitElement) */ | ||
export declare function reactively(prototype: HasReactiveInternal, name: string): any; | ||
export declare function reactively(): (prototype: HasReactiveInternal, name: string) => any; | ||
export declare function reactively(params: ReactivelyParams): (prototype: HasReactiveInternal, name: string) => any; | ||
/** Classes that contain `@reactive` properties should extend `HasReactive` | ||
* (or another class that implements the HasReactive contract). | ||
*/ | ||
export declare class HasReactive implements HasReactiveInternal { | ||
__reactive?: Record<string, Reactive<unknown>>; | ||
constructor(); | ||
} | ||
/** Properties added to the instance and prototype as the instance is constructed. */ | ||
interface DecoratedInternal { | ||
__toInstall?: InstallEntry[]; | ||
} | ||
export interface HasReactiveInternal { | ||
__reactive?: Record<string, Reactive<unknown>>; | ||
} | ||
/** Create Reactive nodes for every reactive property. | ||
* | ||
* Motivation: the property decorators fire _before_ the class prototype is constructed, | ||
* so we defer modification of the prototype until the class is initialized. | ||
* The list of property names and descriptions is stored in the prototype in __toInstall | ||
* The Reactive nodes are stored in the __reactive property on the instance. | ||
* | ||
* The map is used to initialize reactive properties in every new reactive object of this class. | ||
* This is called when every new HasReactive instance is constructed. | ||
*/ | ||
export declare const reactivesToInit: WeakMap<object, string[]>; | ||
/** mark a class that contains reactive properties */ | ||
export declare function hasReactive(): (constructor: any) => any; | ||
/** Mark a mutable property that can be tracked for changes. */ | ||
export declare function reactive(prototype: any, name: string): any; | ||
export declare function reactive(): (prototype: any, name: string) => any; | ||
export declare function installReactiveProperty(instance: any, key: string): void; | ||
export declare function createReactives(r: HasReactiveInternal): void; | ||
interface InstallEntry { | ||
key: string; | ||
descriptor: PropertyDescriptor | undefined; | ||
params: ReactivelyParams | undefined; | ||
} | ||
/** Save info about a reactive property for installation on every instance */ | ||
export declare function queueReactiveToInstall(proto: DecoratedInternal, key: string, descriptor?: PropertyDescriptor, params?: ReactivelyParams): void; | ||
export {}; |
"use strict"; | ||
import { Reactive } from "@reactively/core"; | ||
export const reactivesToInit = /* @__PURE__ */ new Map(); | ||
export function hasReactive() { | ||
return function(constructor) { | ||
return class extends constructor { | ||
constructor(...args) { | ||
super(...args); | ||
initializeReactives(this); | ||
} | ||
}; | ||
}; | ||
export function reactively(prototypeOrParams, name, descriptor) { | ||
if (!prototypeOrParams) { | ||
return addReactive; | ||
} | ||
if (Object.getPrototypeOf(prototypeOrParams) === Object.prototype) { | ||
return (proto, key, descriptor2) => addReactive( | ||
proto, | ||
key, | ||
descriptor2, | ||
prototypeOrParams | ||
); | ||
} else { | ||
return addReactive( | ||
prototypeOrParams, | ||
name, | ||
descriptor | ||
); | ||
} | ||
} | ||
function buildReactiveMap(prototype, name) { | ||
if (!reactivesToInit.has(prototype)) | ||
reactivesToInit.set(prototype, []); | ||
const props = reactivesToInit.get(prototype); | ||
props.push(name); | ||
export class HasReactive { | ||
__reactive; | ||
constructor() { | ||
createReactives(this); | ||
} | ||
} | ||
export function reactive(prototype, name) { | ||
if (prototype) | ||
return buildReactiveMap(prototype, name); | ||
else | ||
return buildReactiveMap; | ||
export function createReactives(r) { | ||
const reactives = r.__reactive || (r.__reactive = {}); | ||
for (const { key, descriptor, params } of installList( | ||
r | ||
)) { | ||
const label = `${r.constructor.name}.${key}`; | ||
const effect = params?.effect; | ||
if (descriptor?.get) { | ||
reactives[key] = new Reactive(descriptor.get.bind(r), effect, label); | ||
} else if (typeof descriptor?.value === "function") { | ||
const boundFn = descriptor.value.bind(r); | ||
reactives[key] = new Reactive(boundFn, effect, label); | ||
} else { | ||
const initializer = descriptor?.initializer; | ||
const value = initializer ? initializer.call(r) : void 0; | ||
reactives[key] = new Reactive(value, effect, label); | ||
} | ||
if (params?.equals) | ||
reactives[key].equals = params.equals; | ||
} | ||
} | ||
function initializeReactives(instance) { | ||
const reactiveProto = Object.getPrototypeOf(instance); | ||
const origProto = Object.getPrototypeOf(reactiveProto); | ||
reactivesToInit.get(origProto)?.forEach((key) => { | ||
installReactiveProperty(instance, key); | ||
}); | ||
function installList(d) { | ||
const installEntries = []; | ||
const installKeys = /* @__PURE__ */ new Set(); | ||
for (let proto = d; proto !== Object.prototype; proto = Object.getPrototypeOf(proto)) { | ||
if (proto.hasOwnProperty("__toInstall")) { | ||
proto.__toInstall.forEach((property) => { | ||
if (!installKeys.has(property.key)) { | ||
installKeys.add(property.key); | ||
installEntries.push(property); | ||
} | ||
}); | ||
} | ||
} | ||
return installEntries; | ||
} | ||
export function installReactiveProperty(instance, key) { | ||
const valueOrFn = instance[key]; | ||
let reactive2; | ||
if (typeof valueOrFn === "function") { | ||
reactive2 = new Reactive(valueOrFn.bind(instance)); | ||
instance[key] = () => reactive2.get(); | ||
function addReactive(proto, key, descriptor, params = {}) { | ||
installOneAccessor(proto, key, descriptor); | ||
queueReactiveToInstall(proto, key, descriptor, params); | ||
return {}; | ||
} | ||
export function queueReactiveToInstall(proto, key, descriptor, params = {}) { | ||
if (!proto.hasOwnProperty("__toInstall")) { | ||
Object.defineProperty(proto, "__toInstall", { | ||
value: [] | ||
}); | ||
} | ||
proto.__toInstall.push({ key, descriptor, params }); | ||
} | ||
function installOneAccessor(proto, key, descriptor) { | ||
function reactiveGet() { | ||
return this.__reactive[key].get(); | ||
} | ||
if (descriptor?.get) { | ||
Object.defineProperty(proto, key, { | ||
get: reactiveGet | ||
}); | ||
} else if (typeof descriptor?.value === "function") { | ||
proto[key] = reactiveGet; | ||
} else { | ||
reactive2 = new Reactive(valueOrFn); | ||
Object.defineProperty(instance, key, { | ||
get: reactive2.get.bind(reactive2), | ||
set: reactive2.set.bind(reactive2) | ||
Object.defineProperty(proto, key, { | ||
get: reactiveGet, | ||
set: function(v) { | ||
return this.__reactive[key].set(v); | ||
} | ||
}); | ||
} | ||
} |
{ | ||
"name": "@reactively/decorate", | ||
"version": "0.0.2", | ||
"version": "0.0.3", | ||
"description": "", | ||
@@ -15,3 +15,3 @@ "main": "./dist/decorate.js", | ||
"dependencies": { | ||
"@reactively/core": "0.0.6" | ||
"@reactively/core": "0.0.8" | ||
}, | ||
@@ -18,0 +18,0 @@ "scripts": { |
@@ -11,3 +11,3 @@ # Reactively | ||
```jsx | ||
```ts | ||
/* A lightweight reactive program can look almost like regular javascript programming. | ||
@@ -21,16 +21,14 @@ * | ||
*/ | ||
import { hasReactive, reactive } from "@reactively/decorate"; | ||
@hasReactive() | ||
class ResizeableBuffer { | ||
@reactive size = 0; | ||
@reactive blocks = () => Math.ceil(this.size / 2 ** 12); | ||
@reactive buffer = () => { | ||
class ResizeableBuffer extends HasReactive { | ||
@reactively size = 0; | ||
@reactively get blocks() { return Math.ceil(this.size / 2 ** 12); } | ||
@reactively get buffer(): any { | ||
const newBuf = Buffer.allocUnsafe(this.blocks * 2 ** 12); | ||
this._buf && newBuf.copy(this.buf); | ||
this._buf && newBuf.copy(this._buf); | ||
return (this._buf = newBuf); | ||
}; | ||
} | ||
private _buf: any; | ||
} | ||
b = new ResizeableBuffer(); | ||
const b = new ResizeableBuffer(); | ||
b.size = 10 ** 5; | ||
@@ -40,4 +38,4 @@ | ||
* which is inefficient. A reactive system will allocate only once. */ | ||
b.buffer().fill(-1); | ||
b.buffer().setAt(0, 100); | ||
b.buffer.fill(-1); | ||
b.buffer.setAt(0, 100); | ||
@@ -47,3 +45,3 @@ /* A reactive system can find other efficiencies. Here's one example: */ | ||
b.size += 1; // grow the number of elements, but blocks doesn't change | ||
b.buffer(); // no new buffer allocated here! | ||
``` | ||
b.buffer; // no new buffer allocated here! | ||
``` |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
6452
133
44
1
+ Added@reactively/core@0.0.8(transitive)
- Removed@reactively/core@0.0.6(transitive)
Updated@reactively/core@0.0.8