diff --git a/index.ts b/index.ts index 671ea39..a38d75e 100644 --- a/index.ts +++ b/index.ts @@ -26,8 +26,16 @@ export interface MacroableConstructorContract { * 2. Can define singleton getters. */ export abstract class Macroable { - protected static _macros: MacroableMap = {} - protected static _getters: MacroableMap = {} + protected static macros: MacroableMap = {} + protected static getters: MacroableMap = {} + + constructor () { + if (!this.constructor.hasOwnProperty('macros') || !this.constructor.hasOwnProperty('getters')) { + throw new Error( + 'Set static properties "macros = {}" and "getters = {}" on the class for the macroable to work.', + ) + } + } /** * Add a macro to the class. This method is a better to manually adding @@ -43,7 +51,7 @@ export abstract class Macroable { * ``` */ public static macro (name: string, callback: MacroableFn) { - this._macros[name] = callback + this.macros[name] = callback this.prototype[name] = callback } @@ -51,7 +59,7 @@ export abstract class Macroable { * Return the existing macro or null if it doesn't exists */ public static getMacro (name: string): MacroableFn | undefined { - return this._macros[name] + return this.macros[name] } /** @@ -92,7 +100,7 @@ export abstract class Macroable { return this[propName] } : callback - this._getters[name] = wrappedCallback + this.getters[name] = wrappedCallback Object.defineProperty(this.prototype, name, { get: wrappedCallback, @@ -105,7 +113,7 @@ export abstract class Macroable { * Return the existing getter or null if it doesn't exists */ public static getGetter (name: string): MacroableFn | undefined { - return this._getters[name] + return this.getters[name] } /** @@ -119,9 +127,9 @@ export abstract class Macroable { * Cleanup getters and macros from the class */ public static hydrate () { - Object.keys(this._macros).forEach((key) => Reflect.deleteProperty(this.prototype, key)) - Object.keys(this._getters).forEach((key) => Reflect.deleteProperty(this.prototype, key)) - this._macros = {} - this._getters = {} + Object.keys(this.macros).forEach((key) => Reflect.deleteProperty(this.prototype, key)) + Object.keys(this.getters).forEach((key) => Reflect.deleteProperty(this.prototype, key)) + this.macros = {} + this.getters = {} } } diff --git a/test.ts b/test.ts index 2234bb2..d3008e4 100644 --- a/test.ts +++ b/test.ts @@ -11,11 +11,11 @@ import test from 'japa' import { Macroable } from './index' class Parent extends Macroable {} -Parent['_macros'] = {} -Parent['_getter'] = {} +Parent['macros'] = {} +Parent['getters'] = {} test.group('Macroable', (group) => { - group.beforeEach(() => { + group.afterEach(() => { Parent.hydrate() }) @@ -70,20 +70,20 @@ test.group('Macroable', (group) => { Parent.hydrate() - assert.deepEqual(Parent['_macros'], {}) - assert.deepEqual(Parent['_getters'], {}) + assert.deepEqual(Parent['macros'], {}) + assert.deepEqual(Parent['getters'], {}) assert.equal(new Parent()['foo'], undefined) assert.equal(new Parent()['bar'], undefined) }) test('static methods should not be shared', (assert) => { class Foo extends Macroable {} - Foo['_macros'] = {} - Foo['_getters'] = {} + Foo['macros'] = {} + Foo['getters'] = {} class Bar extends Macroable {} - Bar['_macros'] = {} - Bar['_getters'] = {} + Bar['macros'] = {} + Bar['getters'] = {} Foo.macro('foo', function foo () {}) assert.isFunction(Foo.getMacro('foo')) @@ -124,4 +124,13 @@ test.group('Macroable', (group) => { assert.equal(m1['foo'], 'bar') assert.equal(getterCalledCounts, 2) }) + + test('raise exception when macros and getters are not initiated as objects', (assert) => { + class Foo extends Macroable {} + const fn = () => new Foo() + assert.throw( + fn, + 'Set static properties "macros = {}" and "getters = {}" on the class for the macroable to work.', + ) + }) })