import { PropertyReference } from './references/descriptors'; import RootReference from './references/root'; import { MetaOptions } from './types'; import { Dict, DictSet, HasGuid, Set, dict } from '@glimmer/util'; import { RootReferenceFactory, PathReferenceFactory, Meta as IMeta, RootReference as IRootReference } from './types'; import { PathReference as IPathReference, VOLATILE_TAG } from '@glimmer/reference'; import { InnerReferenceFactory } from './references/descriptors'; const NOOP_DESTROY = { destroy() {} }; class ConstPath implements IPathReference { private parent: any; private property: string; public tag = VOLATILE_TAG; constructor(parent: any, property: string) { this.parent = parent; } chain() { return NOOP_DESTROY; } notify() {} value() { return this.parent[this.property]; } get(prop: string): IPathReference { return new ConstPath(this.parent[this.property], prop); } } class ConstRoot implements IRootReference { private inner: any; public tag = VOLATILE_TAG; constructor(value) { this.inner = value; } update(inner: any) { this.inner = inner; } chain() { return NOOP_DESTROY; } notify() {} value(): any { return this.inner; } referenceFromParts(parts: string[]): IPathReference { throw new Error("Not implemented"); } chainFor(prop: string): IPathReference { throw new Error("Not implemented"); } get(prop: string): IPathReference { return new ConstPath(this.inner, prop); } } class ConstMeta /*implements IMeta*/ { private object: any; constructor(object: any) { this.object = object; } root(): ConstRoot { return new ConstRoot(this.object); } } export const CLASS_META = "df8be4c8-4e89-44e2-a8f9-550c8dacdca7"; const hasOwnProperty = Object.hasOwnProperty; class Meta implements IMeta, HasGuid { static for(obj: any): IMeta { if (obj === null || obj === undefined) return new Meta(obj, {}); if (hasOwnProperty.call(obj, '_meta') && obj._meta) return obj._meta; if (!Object.isExtensible(obj)) return new ConstMeta(obj); let MetaToUse: typeof Meta = Meta; if (obj.constructor && obj.constructor[CLASS_META]) { let classMeta: ClassMeta = obj.constructor[CLASS_META]; MetaToUse = classMeta.InstanceMetaConstructor; } else if (obj[CLASS_META]) { MetaToUse = obj[CLASS_META].InstanceMetaConstructor; } return (obj._meta = new MetaToUse(obj, {})); } static exists(obj: any): boolean { return typeof obj === 'object' && obj._meta; } static metadataForProperty(key: string): any { return null; } private object: any; private RootReferenceFactory: RootReferenceFactory; private DefaultPathReferenceFactory: InnerReferenceFactory; private rootCache: IRootReference; private references: Dict & HasGuid>> = null; public _guid; protected slots: Dict = null; protected referenceTypes: Dict> = null; protected propertyMetadata: Dict = null; constructor(object: any, { RootReferenceFactory, DefaultPathReferenceFactory }: MetaOptions) { this.object = object; this.RootReferenceFactory = RootReferenceFactory || RootReference; this.DefaultPathReferenceFactory = DefaultPathReferenceFactory || PropertyReference; } addReference(property: string, reference: IPathReference & HasGuid) { let refs = this.references = this.references || dict & HasGuid>>(); let set = refs[property] = refs[property] || new DictSet & HasGuid>(); set.add(reference); } addReferenceTypeFor(property: string, type: PathReferenceFactory) { this.referenceTypes = this.referenceTypes || dict>(); this.referenceTypes[property] = type; } referenceTypeFor(property: string): InnerReferenceFactory { if (!this.referenceTypes) return PropertyReference; return this.referenceTypes[property] || PropertyReference; } removeReference(property: string, reference: IPathReference & HasGuid) { if (!this.references) return; let set = this.references[property]; set.delete(reference); } getReferenceTypes(): Dict> { this.referenceTypes = this.referenceTypes || dict>(); return this.referenceTypes; } referencesFor(property: string): Set> { if (!this.references) return; return this.references[property]; } getSlots() { return (this.slots = this.slots || dict()); } root(): IRootReference { return (this.rootCache = this.rootCache || new this.RootReferenceFactory(this.object)); } } export default Meta; interface ClassMeta { InstanceMetaConstructor: typeof Meta; } export function metaFor(obj: any): IMeta { return Meta.for(obj); }