Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

custom-ability

Package Overview
Dependencies
Maintainers
1
Versions
30
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

custom-ability - npm Package Compare versions

Comparing version 2.0.0-alpha.1 to 2.0.0-alpha.2

75

lib/custom-ability.js

@@ -15,40 +15,55 @@ "use strict";

const injected_on_parent_1 = __importDefault(require("./injected-on-parent"));
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
const skipStaticNames = ['name', 'arguments', 'prototype', 'super_', '__super__', '__proto__'];
const skipProtoNames = ['constructor', '__proto__'];
/**
* Inject methods from NonEnum members of the aObject
* Inject non-enumerable members of the aObject into aTargetClass
*
* @internal
* @param aTargetClass the target class
* @param aObject the NonEnum methods of the object will be injected into aTargetClass
* @param filter
* @param isStatic Whether the injected methods on the aObject is static
* @returns already injected method name list
* @param aObject the non-enumerable members of the object will be injected into aTargetClass
* @param filter It'll be injected only when filter callback function return true, if exists
* @param isStatic Whether is static members
* @returns already injected members name list
*/
function injectMethodsFromNonEnum(aTargetClass, aObject, filter, isStatic) {
function injectMembersFromNonEnum(aTargetClass, aObject, filter, isStatic) {
const nonEnumNames = (0, get_non_enumerable_names_1.getNonEnumerableNames)(aObject);
const result = [];
nonEnumNames.forEach(function (name) {
let v, vName;
if ((isStatic || name !== 'constructor') && (0, function_1.default)(v = aObject[name])) {
const is$ = name[0] === '$';
// get rid of the first char '$'
if (is$) {
name = name.substring(1);
const vSkipNames = isStatic ? skipStaticNames : skipProtoNames;
if (vSkipNames.includes(name)) {
return;
}
const desc = getOwnPropertyDescriptor(aObject, name);
const v = desc.value;
const isFn = (0, function_1.default)(v);
const is$ = name[0] === '$';
// get rid of the first char '$'
if (is$) {
name = name.substring(1);
}
const vName = isStatic ? '@' + name : name;
if (filter && !filter(vName)) {
return;
}
if (desc.get === undefined && desc.set === undefined && v === undefined) {
return;
}
if (!desc.get && isFn) {
if ((0, function_1.default)(aTargetClass[name])) {
(0, injectMethod_1.default)(aTargetClass, name, v);
result.push(vName);
return;
}
vName = isStatic ? '@' + name : name;
if (!filter || filter(vName)) {
if ((0, function_1.default)(aTargetClass[name])) {
(0, injectMethod_1.default)(aTargetClass, name, v);
else if (aTargetClass[name] != null) {
throw new TypeError('the same non-null name is not function:' + name);
}
else {
if (is$ && aObject[name]) {
desc.value = aObject[name];
}
else if (aTargetClass[name] != null) {
throw new TypeError('the same non-null name is not function:' + name);
}
else {
if (is$ && aObject[name]) {
v = aObject[name];
}
aTargetClass[name] = v;
}
result.push(name);
}
}
(0, defineProperty_1.default)(aTargetClass, name, undefined, desc);
result.push(name);
});

@@ -111,3 +126,3 @@ return result;

}
vExcludes = injectMethodsFromNonEnum(aClass, AbilityClass, null, true);
vExcludes = injectMembersFromNonEnum(aClass, AbilityClass, null, true);
(0, extend_1.default)(aClass, AbilityClass, function (k) {

@@ -120,3 +135,3 @@ return !(vExcludes.indexOf(k) >= 0);

if (!vInjectedOnParent) {
vExcludes = injectMethodsFromNonEnum(aClassPrototype, AbilityClass.prototype);
vExcludes = injectMembersFromNonEnum(aClassPrototype, AbilityClass.prototype);
(0, extend_1.default)(aClassPrototype, AbilityClass.prototype, function (k) {

@@ -159,4 +174,4 @@ return !(vExcludes.indexOf(k) >= 0);

};
let vAbilities = injectMethodsFromNonEnum(aClass, AbilityClass, vGenFilter(true), true);
vAbilities = vAbilities.concat(injectMethodsFromNonEnum(aClass.prototype, AbilityClass.prototype, vGenFilter()));
let vAbilities = injectMembersFromNonEnum(aClass, AbilityClass, vGenFilter(true), true);
vAbilities = vAbilities.concat(injectMembersFromNonEnum(aClass.prototype, AbilityClass.prototype, vGenFilter()));
vExcludes = vExcludes.concat(vAbilities);

@@ -163,0 +178,0 @@ vAbilities = undefined;

@@ -9,40 +9,55 @@ import isArray from 'util-ex/lib/is/type/array';

import isInjectedOnParent from './injected-on-parent';
const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor;
const skipStaticNames = ['name', 'arguments', 'prototype', 'super_', '__super__', '__proto__'];
const skipProtoNames = ['constructor', '__proto__'];
/**
* Inject methods from NonEnum members of the aObject
* Inject non-enumerable members of the aObject into aTargetClass
*
* @internal
* @param aTargetClass the target class
* @param aObject the NonEnum methods of the object will be injected into aTargetClass
* @param filter
* @param isStatic Whether the injected methods on the aObject is static
* @returns already injected method name list
* @param aObject the non-enumerable members of the object will be injected into aTargetClass
* @param filter It'll be injected only when filter callback function return true, if exists
* @param isStatic Whether is static members
* @returns already injected members name list
*/
function injectMethodsFromNonEnum(aTargetClass, aObject, filter, isStatic) {
function injectMembersFromNonEnum(aTargetClass, aObject, filter, isStatic) {
const nonEnumNames = getNonEnumNames(aObject);
const result = [];
nonEnumNames.forEach(function (name) {
let v, vName;
if ((isStatic || name !== 'constructor') && isFunction(v = aObject[name])) {
const is$ = name[0] === '$';
// get rid of the first char '$'
if (is$) {
name = name.substring(1);
const vSkipNames = isStatic ? skipStaticNames : skipProtoNames;
if (vSkipNames.includes(name)) {
return;
}
const desc = getOwnPropertyDescriptor(aObject, name);
const v = desc.value;
const isFn = isFunction(v);
const is$ = name[0] === '$';
// get rid of the first char '$'
if (is$) {
name = name.substring(1);
}
const vName = isStatic ? '@' + name : name;
if (filter && !filter(vName)) {
return;
}
if (desc.get === undefined && desc.set === undefined && v === undefined) {
return;
}
if (!desc.get && isFn) {
if (isFunction(aTargetClass[name])) {
injectMethod(aTargetClass, name, v);
result.push(vName);
return;
}
vName = isStatic ? '@' + name : name;
if (!filter || filter(vName)) {
if (isFunction(aTargetClass[name])) {
injectMethod(aTargetClass, name, v);
else if (aTargetClass[name] != null) {
throw new TypeError('the same non-null name is not function:' + name);
}
else {
if (is$ && aObject[name]) {
desc.value = aObject[name];
}
else if (aTargetClass[name] != null) {
throw new TypeError('the same non-null name is not function:' + name);
}
else {
if (is$ && aObject[name]) {
v = aObject[name];
}
aTargetClass[name] = v;
}
result.push(name);
}
}
defineProperty(aTargetClass, name, undefined, desc);
result.push(name);
});

@@ -105,3 +120,3 @@ return result;

}
vExcludes = injectMethodsFromNonEnum(aClass, AbilityClass, null, true);
vExcludes = injectMembersFromNonEnum(aClass, AbilityClass, null, true);
extendFilter(aClass, AbilityClass, function (k) {

@@ -114,3 +129,3 @@ return !(vExcludes.indexOf(k) >= 0);

if (!vInjectedOnParent) {
vExcludes = injectMethodsFromNonEnum(aClassPrototype, AbilityClass.prototype);
vExcludes = injectMembersFromNonEnum(aClassPrototype, AbilityClass.prototype);
extendFilter(aClassPrototype, AbilityClass.prototype, function (k) {

@@ -153,4 +168,4 @@ return !(vExcludes.indexOf(k) >= 0);

};
let vAbilities = injectMethodsFromNonEnum(aClass, AbilityClass, vGenFilter(true), true);
vAbilities = vAbilities.concat(injectMethodsFromNonEnum(aClass.prototype, AbilityClass.prototype, vGenFilter()));
let vAbilities = injectMembersFromNonEnum(aClass, AbilityClass, vGenFilter(true), true);
vAbilities = vAbilities.concat(injectMembersFromNonEnum(aClass.prototype, AbilityClass.prototype, vGenFilter()));
vExcludes = vExcludes.concat(vAbilities);

@@ -157,0 +172,0 @@ vAbilities = undefined;

16

package.json
{
"name": "custom-ability",
"version": "2.0.0-alpha.1",
"version": "2.0.0-alpha.2",
"description": "make custom ability more easy. generate the ability which can be added to any class directly.",

@@ -39,14 +39,14 @@ "homepage": "https://github.com/snowyu/custom-ability.js",

"dependencies": {
"inherits-ex": "^2.1.0-alpha.5",
"util-ex": "^2.0.0-alpha.3"
"inherits-ex": "^2.1.0-alpha.9",
"util-ex": "^2.0.0-alpha.8"
},
"devDependencies": {
"@antfu/eslint-config": "^0.38.4",
"@antfu/eslint-config": "^0.38.5",
"@types/chai": "^4.3.4",
"@types/mocha": "^10.0.1",
"@types/node": "^18.15.11",
"@types/sinon": "^10.0.13",
"@types/sinon": "^10.0.14",
"@types/sinon-chai": "^3.2.9",
"chai": "~4.3.7",
"eslint": "^8.37.0",
"eslint": "^8.38.0",
"eslint-config-prettier": "^8.8.0",

@@ -58,4 +58,4 @@ "eslint-plugin-tsdoc": "^0.2.17",

"ts-node": "^10.9.1",
"typedoc": "^0.23.28",
"typedoc-plugin-markdown": "^3.14.0",
"typedoc": "^0.24.4",
"typedoc-plugin-markdown": "^3.15.1",
"typescript": "^5.0.4"

@@ -62,0 +62,0 @@ },

@@ -25,2 +25,41 @@ # custom-ability [![Build Status](https://img.shields.io/travis/snowyu/custom-ability.js/master.png)](http://travis-ci.org/snowyu/custom-ability.js) [![npm](https://img.shields.io/npm/v/custom-ability.svg)](https://npmjs.org/package/custom-ability) [![downloads](https://img.shields.io/npm/dm/custom-ability.svg)](https://npmjs.org/package/custom-ability) [![license](https://img.shields.io/npm/l/custom-ability.svg)](https://npmjs.org/package/custom-ability)

**Note**: The all non-enumerable members on the Ability class will be injected into the target class.
**Replace Exists Methods**
if you wanna “replace” and call the methods that already exist in a class, you can add the same method name prefixed with "`$`" on the ability class, add call the original method in this way:
```javascript
const makeAbility = require('custom-ability')
class Feature {
// the same method name prefixed with "`$`"
$init() {
// the original method in target class
const Super = this.super
const that = this.self || this
if (Super) {
if (Super.apply(that, arguments) === 'ok') return
}
that._init.apply(that, arguments)
}
_init() {console.log('feature init')}
}
// if the target class has no the init method, it(the enumerable method) will be injected
Feature.prototype.init = function() {this._init.apply(this, arguments)}
const addFeatureTo = makeAbility(Feature)
class My {
}
addFeatureTo(My)
expect(My.prototype.init).toStrictEqual(Feature.prototype.init)
class My2 {
init() {console.log('My2 init')}
}
addFeatureTo(My2)
expect(My2.prototype.init).toStrictEqual(Feature.prototype.$init)
```
## Examples

@@ -160,5 +199,2 @@

**TODO: need to more explain:**
The original `eventable('events-ex/eventable')` is no useful for AbstractObject.
But we wanna the original `eventable('events-ex/eventable')` knows the changes

@@ -165,0 +201,0 @@ and use it automatically.

@@ -10,39 +10,51 @@ import isArray from 'util-ex/lib/is/type/array';

const getOwnPropertyDescriptor = Object.getOwnPropertyDescriptor
const skipStaticNames = ['name', 'arguments', 'prototype', 'super_', '__super__', '__proto__']
const skipProtoNames = ['constructor', '__proto__']
/**
* Inject methods from NonEnum members of the aObject
* Inject non-enumerable members of the aObject into aTargetClass
*
* @internal
* @param aTargetClass the target class
* @param aObject the NonEnum methods of the object will be injected into aTargetClass
* @param filter
* @param isStatic Whether the injected methods on the aObject is static
* @returns already injected method name list
* @param aObject the non-enumerable members of the object will be injected into aTargetClass
* @param filter It'll be injected only when filter callback function return true, if exists
* @param isStatic Whether is static members
* @returns already injected members name list
*/
function injectMethodsFromNonEnum(aTargetClass, aObject, filter?: (name:string)=>boolean, isStatic?: boolean) {
function injectMembersFromNonEnum(aTargetClass, aObject, filter?: (name:string)=>boolean, isStatic?: boolean) {
const nonEnumNames = getNonEnumNames(aObject);
const result = [];
nonEnumNames.forEach(function(name: string) {
let v, vName: string;
if ((isStatic || name !== 'constructor') && isFunction(v = aObject[name])) {
const is$ = name[0] === '$';
// get rid of the first char '$'
if (is$) {
name = name.substring(1);
}
vName = isStatic ? '@' + name : name;
if (!filter || filter(vName)) {
if (isFunction(aTargetClass[name])) {
injectMethod(aTargetClass, name, v);
} else if (aTargetClass[name] != null) {
throw new TypeError('the same non-null name is not function:' + name);
} else {
if (is$ && aObject[name]) {
v = aObject[name];
}
aTargetClass[name] = v;
const vSkipNames = isStatic ? skipStaticNames : skipProtoNames
if (vSkipNames.includes(name)) {return}
const desc = getOwnPropertyDescriptor(aObject, name)
const v = desc.value
const isFn = isFunction(v)
const is$ = name[0] === '$';
// get rid of the first char '$'
if (is$) {
name = name.substring(1);
}
const vName = isStatic ? '@' + name : name;
if (filter && !filter(vName)) {return}
if (desc.get === undefined && desc.set === undefined && v === undefined) {return}
if (!desc.get && isFn) {
if (isFunction(aTargetClass[name])) {
injectMethod(aTargetClass, name, v);
result.push(vName);
return;
} else if (aTargetClass[name] != null) {
throw new TypeError('the same non-null name is not function:' + name);
} else {
if (is$ && aObject[name]) {
desc.value = aObject[name];
}
result.push(name);
}
}
});
defineProperty(aTargetClass, name, undefined, desc)
result.push(name);
});
return result;

@@ -151,3 +163,3 @@ };

}
vExcludes = injectMethodsFromNonEnum(aClass, AbilityClass, null, true);
vExcludes = injectMembersFromNonEnum(aClass, AbilityClass, null, true);
extendFilter(aClass, AbilityClass, function(k) {

@@ -160,3 +172,3 @@ return !(vExcludes.indexOf(k) >= 0);

if (!vInjectedOnParent) {
vExcludes = injectMethodsFromNonEnum(aClassPrototype, AbilityClass.prototype);
vExcludes = injectMembersFromNonEnum(aClassPrototype, AbilityClass.prototype);
extendFilter(aClassPrototype, AbilityClass.prototype, function(k) {

@@ -197,4 +209,4 @@ return !(vExcludes.indexOf(k) >= 0);

let vAbilities = injectMethodsFromNonEnum(aClass, AbilityClass, vGenFilter(true), true);
vAbilities = vAbilities.concat(injectMethodsFromNonEnum(aClass.prototype, AbilityClass.prototype, vGenFilter()));
let vAbilities = injectMembersFromNonEnum(aClass, AbilityClass, vGenFilter(true), true);
vAbilities = vAbilities.concat(injectMembersFromNonEnum(aClass.prototype, AbilityClass.prototype, vGenFilter()));
vExcludes = vExcludes.concat(vAbilities);

@@ -201,0 +213,0 @@ vAbilities = undefined;

@@ -1,2 +0,2 @@

import chai from 'chai';
import chai, { expect } from 'chai';
import sinon from 'sinon';

@@ -85,2 +85,31 @@ import sinonChai from 'sinon-chai';

});
it('should ignore getter attribute', function() {
class MyFeature {
static additionalClassMethod: () => void;
static coreAbilityClassMethod(){};
static get getter(){return 1}
static field = 1
get getter(){return 1}
coreAbilityMethod(){};
additionalAbilityMethod(){};
}
MyFeature.additionalClassMethod = function() {}
const addFeatureTo = customAbility(MyFeature, ['coreAbilityMethod', '@coreAbilityClassMethod']);
class MyClass {
someMethod() {}
}
// inject the static and instance methods to the MyClass.
addFeatureTo(MyClass);
MyClass.should.have.ownProperty('coreAbilityClassMethod')
let prop = Object.getOwnPropertyDescriptor(MyFeature, 'getter')
prop!.get!.should.be.a('function');
Object.getOwnPropertyDescriptor(MyClass, 'getter')!.should.have.ownProperty('get', prop!.get)
prop = Object.getOwnPropertyDescriptor(MyFeature.prototype, 'getter')
Object.getOwnPropertyDescriptor(MyClass.prototype, 'getter')!.should.have.ownProperty('get', prop!.get)
prop = Object.getOwnPropertyDescriptor(MyFeature, 'field')
Object.getOwnPropertyDescriptor(MyClass, 'field')!.should.have.ownProperty('value', 1)
});
it('could use getAbilityClass', function() {

@@ -87,0 +116,0 @@ var My, getAbilityClass, result, testable1;

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc