| { | ||
| "source": "./lib", | ||
| "destination": "./dist/api/esdoc", | ||
| "plugins": [{"name": "esdoc-standard-plugin"}] | ||
| } |
| language: node_js | ||
| node_js: | ||
| - 6.0 | ||
| after_success: npm run coverage |
| /** | ||
| * @module esmodule | ||
| * @author Vyacheslav Aristov <vv.aristov@gmail.com> | ||
| * @license MIT | ||
| */ | ||
| export * from './assembler' |
+145
| import test from 'ava' | ||
| import { Assembler } from '../lib/assembler' | ||
| let undefined | ||
| const { getTargetOf } = Assembler | ||
| class Example extends Object {} | ||
| Example.prototype.foo = null | ||
| Example.prototype.bar = null | ||
| class ExampleAssembler extends Assembler { | ||
| setPropertyFilter(name) { | ||
| return super.setPropertyFilter(name) && name !== 'nom' | ||
| } | ||
| set foo(foo) { | ||
| this.target.foo = foo | ||
| } | ||
| get foo() { | ||
| return this.target.foo | ||
| } | ||
| get target() { | ||
| return getTargetOf(this) | ||
| } | ||
| static get defaultPropertyName() { | ||
| return 'foo' | ||
| } | ||
| static get targetPropertyName() { | ||
| return 'target' | ||
| } | ||
| static get interface() { | ||
| return Example | ||
| } | ||
| } | ||
| test('interface', t => { | ||
| t.is(Assembler.interface, Object) | ||
| }) | ||
| test('defaultPropertyName', t => { | ||
| t.is(Assembler.defaultPropertyName, undefined) | ||
| }) | ||
| test('create', t => { | ||
| t.deepEqual(Assembler.create(), {}) | ||
| }) | ||
| test('getTargetOf', t => { | ||
| const obj = {} | ||
| t.is(Assembler.getTargetOf(obj), null) | ||
| }) | ||
| test('getInstanceOf', t => { | ||
| t.is(Assembler.getInstanceOf({}), null) | ||
| }) | ||
| test('new Assembler + getTargetOf + getInstanceOf', t => { | ||
| const instance = new Assembler | ||
| const target = Assembler.getTargetOf(instance) | ||
| t.deepEqual(target, {}) | ||
| t.true(target instanceof Assembler.interface) | ||
| t.is(Assembler.getTargetOf(target), target) | ||
| t.is(Assembler.getInstanceOf(target), instance) | ||
| t.is(Assembler.getInstanceOf(instance), instance) | ||
| }) | ||
| test('setProperty(name, new String)', t => { | ||
| const instance = new ExampleAssembler | ||
| instance.setProperty('foo', '123') | ||
| t.is(instance.foo, '123') | ||
| t.is(instance.target.foo, '123') | ||
| }) | ||
| test('setProperty(name, undefined)', t => { | ||
| const instance = new ExampleAssembler | ||
| instance.setProperty('foo', undefined) | ||
| t.is(instance.foo, null) | ||
| t.is(instance.target.foo, null) | ||
| }) | ||
| test('setProperty(name, new String) -> setPropertyFallback', t => { | ||
| const instance = new ExampleAssembler | ||
| instance.setProperty('bar', '456') | ||
| t.is(instance.bar, undefined) | ||
| t.is(instance.target.bar, '456') | ||
| }) | ||
| test('setProperty(name, undefined) -> setPropertyFallback', t => { | ||
| const instance = new ExampleAssembler | ||
| instance.setProperty('bar', undefined) | ||
| t.is(instance.bar, undefined) | ||
| t.is(instance.target.bar, null) | ||
| }) | ||
| test('setProperty -> setPropertyFallback -> setPropertyMismatch', t => { | ||
| const instance = new ExampleAssembler | ||
| t.throws(() => instance.setProperty('wiz', '789')) | ||
| t.throws(() => instance.setProperty('wiz', undefined)) | ||
| }) | ||
| test('Assign filtered property', t => { | ||
| const instance = new ExampleAssembler | ||
| instance.assign({ nom : '777' }) | ||
| t.is(instance.nom, undefined) | ||
| t.is(instance.target.nom, undefined) | ||
| }) | ||
| test('Init by defined properties', t => { | ||
| const instance = new ExampleAssembler | ||
| instance.init({ | ||
| foo : '123', | ||
| bar : '456' | ||
| }) | ||
| t.is(instance.foo, '123') | ||
| t.is(instance.target.foo, '123') | ||
| t.is(instance.bar, undefined) | ||
| t.is(instance.target.bar, '456') | ||
| }) | ||
| test('Init by not defined property', t => { | ||
| t.throws(() => new ExampleAssembler({ wiz : '789' })) | ||
| t.throws(() => new ExampleAssembler({ wiz : undefined })) | ||
| }) | ||
| test('Init by the default property', t => { | ||
| const instance = new ExampleAssembler | ||
| instance.init('123') | ||
| t.is(instance.foo, '123') | ||
| t.is(instance.target.foo, '123') | ||
| t.deepEqual(getTargetOf(new Assembler('will be ignored')), {}) | ||
| }) | ||
| test('Init by explicit target setting', t => { | ||
| const target = new Example | ||
| const instance = new ExampleAssembler({ target }) | ||
| t.is(instance.target, target) | ||
| t.throws(() => new ExampleAssembler({ target : 'wrong' })) | ||
| }) |
+109
-77
| let undefined | ||
| const INIT_PROPERTY_NAME = 'init' | ||
| const INSTANCE_PROPERTY_NAME = '__instance__' | ||
| const TARGET_PROPERTY_NAME = '__target__' | ||
| const TYPE_UNDEFINED = 'undefined' | ||
| const storage = new WeakMap | ||
| const key = Symbol() | ||
| /** | ||
| * @summary Assembler is a simple class, which helps to assemble another JavaScript objects. | ||
| */ | ||
| export class Assembler { | ||
| /** | ||
| * Create the assembler instance and assemble the specified object | ||
| * Initializes the object by all passed init arguments | ||
| * Create and initialize target by specified initialing object | ||
| * @param {*} [init] | ||
| */ | ||
| constructor(init) { | ||
| this.assemble(init) | ||
| } | ||
| /** | ||
| * Instantiate and initialize the specified object | ||
| * @param {*} [init] initializing dictionary | ||
| * @returns {Object|*} | ||
| */ | ||
| assemble(init) { | ||
| this.create(init) | ||
| this.init(init) | ||
| return this[TARGET_PROPERTY_NAME] | ||
| } | ||
| /** | ||
| * Create the defined object | ||
| * @param {*} [init] initializing dictionary | ||
| * Create target and link it with the assembler instance | ||
| * @param {*} [init] initializing object | ||
| */ | ||
| create(init) { | ||
| this.setInstanceOf(init && init.constructor === Object? | ||
| this.constructor.create(init) : | ||
| this.constructor.create()) | ||
| const constructor = this.constructor | ||
| const { targetPropertyName } = constructor | ||
| if(targetPropertyName !== undefined | ||
| && init && init.constructor === Object && init.hasOwnProperty(targetPropertyName)) { | ||
| constructor.setTargetOf(this, init[targetPropertyName]) | ||
| } | ||
| else { | ||
| constructor.setTargetOf(this, init && init.constructor === Object? | ||
| constructor.create(init) : | ||
| constructor.create()) | ||
| } | ||
| } | ||
| /** | ||
| * This is a handshake between the Assembler instance and a target | ||
| * @param {Object|*} target | ||
| * Initialize target by specified initializing object | ||
| * @param {*} [init] initializing object | ||
| */ | ||
| setInstanceOf(target) { | ||
| this[TARGET_PROPERTY_NAME] = target | ||
| target[INSTANCE_PROPERTY_NAME] = this | ||
| } | ||
| /** | ||
| * Initialize target with the specified properties | ||
| * @param {*} [init] initializing dictionary | ||
| */ | ||
| init(init) { | ||
@@ -55,17 +45,22 @@ if(init && init.constructor === Object) { | ||
| } | ||
| else if(typeof init !== TYPE_UNDEFINED) { | ||
| this.assign({ [this.constructor.initPropertyName] : init }) | ||
| else if(init !== undefined) { | ||
| const { defaultPropertyName } = this.constructor | ||
| if(defaultPropertyName !== undefined) { | ||
| this.setProperty(defaultPropertyName, init) | ||
| } | ||
| } | ||
| else this.assign({}) | ||
| } | ||
| /** | ||
| * Assign specified properties on the instance if they are not static | ||
| * Assign properties from specified initializing object | ||
| * @param {{}} init | ||
| */ | ||
| assign(init) { | ||
| const { constructor } = this | ||
| for(let prop in init) { | ||
| if(init.hasOwnProperty(prop) && !(prop in constructor)) { | ||
| this.setProperty(prop, init[prop]) | ||
| const { defaultPropertyName } = this.constructor | ||
| if(defaultPropertyName !== undefined && init.hasOwnProperty(defaultPropertyName)) { | ||
| this.setProperty(defaultPropertyName, init[defaultPropertyName]) | ||
| } | ||
| for(const name in init) { | ||
| if(init.hasOwnProperty(name) && this.setPropertyFilter(name)) { | ||
| this.setProperty(name, init[name]) | ||
| } | ||
@@ -76,21 +71,27 @@ } | ||
| /** | ||
| * Set a single property if it is in this and not undefined or fallback otherwise | ||
| * @param {String} name | ||
| * @param {String} value | ||
| * Set a single property if it is in this and is not undefined or fallback otherwise | ||
| * @param {string} name | ||
| * @param {*} value | ||
| */ | ||
| setProperty(name, value) { | ||
| if(value !== undefined) { | ||
| if(name in this) this[name] = value | ||
| else this.setPropertyFallback(name, value) | ||
| if(name in this) { | ||
| if(value !== undefined) { | ||
| this[name] = value | ||
| } | ||
| } | ||
| else this.setPropertyFallback(name, value) | ||
| } | ||
| /** | ||
| * Set a signle property on target if it is in target or mismatch otherwise | ||
| * @param name | ||
| * @param value | ||
| * Set a single property on target if it is in target and is not undefined or mismatch otherwise | ||
| * @param {string} name | ||
| * @param {*} value | ||
| */ | ||
| setPropertyFallback(name, value) { | ||
| const target = this[TARGET_PROPERTY_NAME] | ||
| if(name in target) target[name] = value | ||
| const target = this[key] | ||
| if(name in target) { | ||
| if(value !== undefined) { | ||
| target[name] = value | ||
| } | ||
| } | ||
| else this.setPropertyMismatch(name) | ||
@@ -100,37 +101,78 @@ } | ||
| /** | ||
| * Throw the "set property mismatch" error | ||
| * @param {String} prop mismatched property name | ||
| * @param {string} name | ||
| * @returns {boolean} | ||
| */ | ||
| setPropertyMismatch(prop) { | ||
| const name = this.constructor.name | ||
| throw Error(`The property "${ prop }" is not found on the ${ name } instance`) | ||
| setPropertyFilter(name) { | ||
| const { defaultPropertyName, targetPropertyName } = this.constructor | ||
| return name !== targetPropertyName && name !== defaultPropertyName | ||
| } | ||
| /** | ||
| * Create the interface prototype object and define it's properties if they are specified | ||
| * Handle a mismatched property assignment | ||
| * @param {string} name mismatched property name | ||
| */ | ||
| setPropertyMismatch(name) { | ||
| throw Error(`The property '${ name }' is not found on the '${ this.constructor.name }' instance.`) | ||
| } | ||
| /** | ||
| * Create a target specified by interface | ||
| * @param {{}} [init] | ||
| * @returns {Object} | ||
| */ | ||
| static create({ properties } = this) { | ||
| return Object.create(this.interface.prototype, properties) | ||
| static create(init) { | ||
| return new this.interface | ||
| } | ||
| /** | ||
| * Get an instance of the target or an instance itself | ||
| * @param {Assembler|Object|*} target | ||
| * Get an instance of the target or the instance itself or null if it's not a target | ||
| * @param {Assembler|Object|*} object | ||
| * @returns {Assembler|*|null} | ||
| */ | ||
| static getInstanceOf(target) { | ||
| return target instanceof Assembler? target : target[INSTANCE_PROPERTY_NAME] | ||
| static getInstanceOf(object) { | ||
| return object instanceof Assembler? | ||
| object : | ||
| storage.get(object) || null | ||
| } | ||
| /** | ||
| * This property name is used for the `init.constructor !== Object` cases | ||
| * @returns {String} | ||
| * Get a target of the instance or the target itself or null if it's not a target | ||
| * @param {Assembler|Object|*} object | ||
| * @returns {Object|*|null} | ||
| */ | ||
| static get initPropertyName() { | ||
| return INIT_PROPERTY_NAME | ||
| static getTargetOf(object) { | ||
| return object instanceof Assembler? | ||
| object[key] : | ||
| storage.has(object)? object : null | ||
| } | ||
| /** | ||
| * Link target and the assembler instance together | ||
| * @param {Assembler|*} instance | ||
| * @param {Object|*} target | ||
| */ | ||
| static setTargetOf(instance, target) { | ||
| const _interface = instance.constructor.interface | ||
| if(target instanceof _interface || target.constructor === _interface) { | ||
| instance[key] = target | ||
| storage.set(target, instance) | ||
| } | ||
| else throw TypeError(`Failed to execute 'setTargetOf' on '${ instance.constructor.name }': target is not of expected type.`) | ||
| } | ||
| /** | ||
| * This property name is used for the `init.constructor !== Object` case | ||
| * @returns {undefined|string} | ||
| * @abstract | ||
| */ | ||
| static get defaultPropertyName() {} | ||
| /** | ||
| * This property name may be used to explicitly set target of the instance | ||
| * @returns {undefined|string} | ||
| * @abstract | ||
| */ | ||
| static get targetPropertyName() {} | ||
| /** | ||
| * A default interface of a target object | ||
@@ -142,12 +184,2 @@ * @returns {ObjectConstructor|*} | ||
| } | ||
| /** | ||
| * Default undefined properties | ||
| * @returns {undefined|*} | ||
| */ | ||
| static get properties() { | ||
| return undefined | ||
| } | ||
| } | ||
| Object.defineProperty(Assembler.prototype, TARGET_PROPERTY_NAME, { writable : true, value : null }) |
+53
-6
| { | ||
| "name": "esmodule", | ||
| "version": "0.0.1", | ||
| "version": "0.0.2", | ||
| "description": "ES object assembler library", | ||
| "main": "lib/index.js", | ||
| "scripts": { | ||
| "test": "echo \"Error: no test specified\" && exit 1" | ||
| "test": "nyc ava", | ||
| "coverage": "nyc report --reporter=text-lcov | coveralls", | ||
| "api": "rm -rf dist/api && npm run jsdoc && npm run esdoc", | ||
| "esdoc": "esdoc", | ||
| "jsdoc": "jsdoc lib/* -d dist/api/jsdoc --verbose" | ||
| }, | ||
@@ -17,8 +21,9 @@ "repository": { | ||
| "target", | ||
| "assemble", | ||
| "create", | ||
| "init", | ||
| "assign", | ||
| "interface", | ||
| "properties" | ||
| "property", | ||
| "fallback", | ||
| "mismatch", | ||
| "interface" | ||
| ], | ||
@@ -30,3 +35,45 @@ "author": "Vyacheslav Aristov <vv.aristov@gmail.ru>", | ||
| }, | ||
| "homepage": "https://github.com/aristov/esmodule#readme" | ||
| "homepage": "https://github.com/aristov/esmodule#readme", | ||
| "devDependencies": { | ||
| "ava": "^0.25.0", | ||
| "babel-plugin-istanbul": "^4.1.6", | ||
| "babel-preset-env": "^1.7.0", | ||
| "babel-register": "^6.26.0", | ||
| "coveralls": "^3.0.1", | ||
| "esdoc": "^1.1.0", | ||
| "esdoc-standard-plugin": "^1.0.0", | ||
| "jsdoc": "^3.5.5", | ||
| "nyc": "^12.0.2" | ||
| }, | ||
| "ava": { | ||
| "verbose": true | ||
| }, | ||
| "babel": { | ||
| "presets": [ | ||
| "env" | ||
| ], | ||
| "ignore": "test.js", | ||
| "env": { | ||
| "development": { | ||
| "sourceMaps": "inline" | ||
| }, | ||
| "test": { | ||
| "plugins": [ | ||
| "istanbul" | ||
| ] | ||
| } | ||
| } | ||
| }, | ||
| "nyc": { | ||
| "require": [ | ||
| "babel-register" | ||
| ], | ||
| "reporter": [ | ||
| "lcov", | ||
| "text" | ||
| ], | ||
| "sourceMap": false, | ||
| "instrument": false, | ||
| "report-dir": "dist/coverage" | ||
| } | ||
| } |
+8
-2
| # esmodule | ||
| [](https://www.npmjs.com/package/esmodule) | ||
| [](https://travis-ci.org/aristov/esmodule) | ||
| [](https://coveralls.io/github/aristov/esmodule?branch=master) | ||
| [](https://david-dm.org/aristov/esmodule) | ||
| [](https://david-dm.org/aristov/esmodule?type=dev) | ||
| _work in progress_ | ||
@@ -13,3 +19,3 @@ | ||
| ## Development | ||
| ## Testing | ||
@@ -20,3 +26,3 @@ ``` | ||
| npm install | ||
| npm run webpack | ||
| npm test | ||
| ``` | ||
@@ -23,0 +29,0 @@ |
Sorry, the diff of this file is not supported yet
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
No tests
QualityPackage does not have any tests. This is a strong signal of a poorly maintained or low quality package.
Found 1 instance in 1 package
12967
106.48%8
60%294
119.4%1
-50%31
24%9
Infinity%1
Infinity%