feat(mixin): split a class in independant parts (#3415)
This commit is contained in:
parent
42591bd4bd
commit
cfd6fd722a
3
@xen-orchestra/mixin/.babelrc.js
Normal file
3
@xen-orchestra/mixin/.babelrc.js
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
module.exports = require('../../@xen-orchestra/babel-config')(
|
||||||
|
require('./package.json')
|
||||||
|
)
|
24
@xen-orchestra/mixin/.npmignore
Normal file
24
@xen-orchestra/mixin/.npmignore
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
/benchmark/
|
||||||
|
/benchmarks/
|
||||||
|
*.bench.js
|
||||||
|
*.bench.js.map
|
||||||
|
|
||||||
|
/examples/
|
||||||
|
example.js
|
||||||
|
example.js.map
|
||||||
|
*.example.js
|
||||||
|
*.example.js.map
|
||||||
|
|
||||||
|
/fixture/
|
||||||
|
/fixtures/
|
||||||
|
*.fixture.js
|
||||||
|
*.fixture.js.map
|
||||||
|
*.fixtures.js
|
||||||
|
*.fixtures.js.map
|
||||||
|
|
||||||
|
/test/
|
||||||
|
/tests/
|
||||||
|
*.spec.js
|
||||||
|
*.spec.js.map
|
||||||
|
|
||||||
|
__snapshots__/
|
49
@xen-orchestra/mixin/README.md
Normal file
49
@xen-orchestra/mixin/README.md
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
# ${pkg.name} [](https://travis-ci.org/${pkg.shortGitHubPath})
|
||||||
|
|
||||||
|
> ${pkg.description}
|
||||||
|
|
||||||
|
## Install
|
||||||
|
|
||||||
|
Installation of the [npm package](https://npmjs.org/package/${pkg.name}):
|
||||||
|
|
||||||
|
```
|
||||||
|
> npm install --save ${pkg.name}
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
**TODO**
|
||||||
|
|
||||||
|
## Development
|
||||||
|
|
||||||
|
```
|
||||||
|
# Install dependencies
|
||||||
|
> yarn
|
||||||
|
|
||||||
|
# Run the tests
|
||||||
|
> yarn test
|
||||||
|
|
||||||
|
# Continuously compile
|
||||||
|
> yarn dev
|
||||||
|
|
||||||
|
# Continuously run the tests
|
||||||
|
> yarn dev-test
|
||||||
|
|
||||||
|
# Build for production (automatically called by npm install)
|
||||||
|
> yarn build
|
||||||
|
```
|
||||||
|
|
||||||
|
## Contributions
|
||||||
|
|
||||||
|
Contributions are *very* welcomed, either on the documentation or on
|
||||||
|
the code.
|
||||||
|
|
||||||
|
You may:
|
||||||
|
|
||||||
|
- report any [issue](${pkg.bugs})
|
||||||
|
you've encountered;
|
||||||
|
- fork and create a pull request.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
${pkg.license} © [${pkg.author.name}](${pkg.author.url})
|
49
@xen-orchestra/mixin/package.json
Normal file
49
@xen-orchestra/mixin/package.json
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
{
|
||||||
|
"name": "@xen-orchestra/mixin",
|
||||||
|
"version": "0.0.0",
|
||||||
|
"license": "ISC",
|
||||||
|
"description": "",
|
||||||
|
"keywords": [],
|
||||||
|
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/@xen-orchestra/mixin",
|
||||||
|
"bugs": "https://github.com/vatesfr/xen-orchestra/issues",
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||||
|
},
|
||||||
|
"author": {
|
||||||
|
"name": "Julien Fontanet",
|
||||||
|
"email": "julien.fontanet@vates.fr"
|
||||||
|
},
|
||||||
|
"preferGlobal": false,
|
||||||
|
"main": "dist/",
|
||||||
|
"bin": {},
|
||||||
|
"files": [
|
||||||
|
"dist/"
|
||||||
|
],
|
||||||
|
"browserslist": [
|
||||||
|
">2%"
|
||||||
|
],
|
||||||
|
"engines": {
|
||||||
|
"node": ">=6"
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"bind-property-descriptor": "^1.0.0"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@babel/cli": "7.0.0",
|
||||||
|
"@babel/core": "7.0.0",
|
||||||
|
"@babel/preset-env": "7.0.0",
|
||||||
|
"babel-plugin-dev": "^1.0.0",
|
||||||
|
"babel-plugin-lodash": "^3.3.2",
|
||||||
|
"cross-env": "^5.1.3",
|
||||||
|
"rimraf": "^2.6.2"
|
||||||
|
},
|
||||||
|
"scripts": {
|
||||||
|
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||||
|
"clean": "rimraf dist/",
|
||||||
|
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||||
|
"prebuild": "yarn run clean",
|
||||||
|
"predev": "yarn run prebuild",
|
||||||
|
"prepublishOnly": "yarn run build"
|
||||||
|
}
|
||||||
|
}
|
130
@xen-orchestra/mixin/src/index.js
Normal file
130
@xen-orchestra/mixin/src/index.js
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
import { getBoundPropertyDescriptor } from 'bind-property-descriptor'
|
||||||
|
|
||||||
|
// ===================================================================
|
||||||
|
|
||||||
|
const { defineProperties, getOwnPropertyDescriptor } = Object
|
||||||
|
|
||||||
|
const isIgnoredProperty = name => name[0] === '_' || name === 'constructor'
|
||||||
|
|
||||||
|
const IGNORED_STATIC_PROPERTIES = {
|
||||||
|
__proto__: null,
|
||||||
|
|
||||||
|
arguments: true,
|
||||||
|
caller: true,
|
||||||
|
length: true,
|
||||||
|
name: true,
|
||||||
|
prototype: true,
|
||||||
|
}
|
||||||
|
const isIgnoredStaticProperty = name => name in IGNORED_STATIC_PROPERTIES
|
||||||
|
|
||||||
|
const ownKeys =
|
||||||
|
(typeof Reflect !== 'undefined' && Reflect.ownKeys) ||
|
||||||
|
(({ getOwnPropertyNames: names, getOwnPropertySymbols: symbols }) =>
|
||||||
|
symbols !== undefined ? obj => names(obj).concat(symbols(obj)) : names)(
|
||||||
|
Object
|
||||||
|
)
|
||||||
|
|
||||||
|
// -------------------------------------------------------------------
|
||||||
|
|
||||||
|
const mixin = Mixins => Class => {
|
||||||
|
if (__DEV__ && !Array.isArray(Mixins)) {
|
||||||
|
throw new TypeError('Mixins should be an array')
|
||||||
|
}
|
||||||
|
|
||||||
|
const { name } = Class
|
||||||
|
|
||||||
|
// Copy properties of plain object mix-ins to the prototype.
|
||||||
|
{
|
||||||
|
const allMixins = Mixins
|
||||||
|
Mixins = []
|
||||||
|
const { prototype } = Class
|
||||||
|
const descriptors = { __proto__: null }
|
||||||
|
allMixins.forEach(Mixin => {
|
||||||
|
if (typeof Mixin === 'function') {
|
||||||
|
Mixins.push(Mixin)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const prop of ownKeys(Mixin)) {
|
||||||
|
if (__DEV__ && prop in prototype) {
|
||||||
|
throw new Error(`${name}#${prop} is already defined`)
|
||||||
|
}
|
||||||
|
|
||||||
|
;(descriptors[prop] = getOwnPropertyDescriptor(
|
||||||
|
Mixin,
|
||||||
|
prop
|
||||||
|
)).enumerable = false // Object methods are enumerable but class methods are not.
|
||||||
|
}
|
||||||
|
})
|
||||||
|
defineProperties(prototype, descriptors)
|
||||||
|
}
|
||||||
|
|
||||||
|
const n = Mixins.length
|
||||||
|
|
||||||
|
function DecoratedClass (...args) {
|
||||||
|
const instance = new Class(...args)
|
||||||
|
|
||||||
|
for (let i = 0; i < n; ++i) {
|
||||||
|
const Mixin = Mixins[i]
|
||||||
|
const { prototype } = Mixin
|
||||||
|
const mixinInstance = new Mixin(instance, ...args)
|
||||||
|
const descriptors = { __proto__: null }
|
||||||
|
const props = ownKeys(prototype)
|
||||||
|
for (let j = 0, m = props.length; j < m; ++j) {
|
||||||
|
const prop = props[j]
|
||||||
|
|
||||||
|
if (isIgnoredProperty(prop)) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if (prop in instance) {
|
||||||
|
throw new Error(`${name}#${prop} is already defined`)
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptors[prop] = getBoundPropertyDescriptor(
|
||||||
|
prototype,
|
||||||
|
prop,
|
||||||
|
mixinInstance
|
||||||
|
)
|
||||||
|
}
|
||||||
|
defineProperties(instance, descriptors)
|
||||||
|
}
|
||||||
|
|
||||||
|
return instance
|
||||||
|
}
|
||||||
|
|
||||||
|
// Copy original and mixed-in static properties on Decorator class.
|
||||||
|
const descriptors = { __proto__: null }
|
||||||
|
ownKeys(Class).forEach(prop => {
|
||||||
|
let descriptor
|
||||||
|
if (
|
||||||
|
!(
|
||||||
|
isIgnoredStaticProperty(prop) &&
|
||||||
|
// if they already exist...
|
||||||
|
(descriptor = getOwnPropertyDescriptor(DecoratedClass, prop)) !==
|
||||||
|
undefined &&
|
||||||
|
// and are not configurable.
|
||||||
|
!descriptor.configurable
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
descriptors[prop] = getOwnPropertyDescriptor(Class, prop)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
Mixins.forEach(Mixin => {
|
||||||
|
ownKeys(Mixin).forEach(prop => {
|
||||||
|
if (isIgnoredStaticProperty(prop)) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (__DEV__ && prop in descriptors) {
|
||||||
|
throw new Error(`${name}.${prop} is already defined`)
|
||||||
|
}
|
||||||
|
|
||||||
|
descriptors[prop] = getOwnPropertyDescriptor(Mixin, prop)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
defineProperties(DecoratedClass, descriptors)
|
||||||
|
|
||||||
|
return DecoratedClass
|
||||||
|
}
|
||||||
|
export { mixin as default }
|
@ -20,6 +20,7 @@
|
|||||||
|
|
||||||
### Released packages
|
### Released packages
|
||||||
|
|
||||||
|
- @xen-orchestra/mixin v0.0.0
|
||||||
- @xen-orchestra/emit-async v0.0.0
|
- @xen-orchestra/emit-async v0.0.0
|
||||||
- xo-server v5.27.0
|
- xo-server v5.27.0
|
||||||
- xo-web v5.27.0
|
- xo-web v5.27.0
|
||||||
|
@ -35,12 +35,12 @@
|
|||||||
"@xen-orchestra/cron": "^1.0.3",
|
"@xen-orchestra/cron": "^1.0.3",
|
||||||
"@xen-orchestra/emit-async": "^0.0.0",
|
"@xen-orchestra/emit-async": "^0.0.0",
|
||||||
"@xen-orchestra/fs": "^0.3.0",
|
"@xen-orchestra/fs": "^0.3.0",
|
||||||
|
"@xen-orchestra/mixin": "^0.0.0",
|
||||||
"ajv": "^6.1.1",
|
"ajv": "^6.1.1",
|
||||||
"app-conf": "^0.5.0",
|
"app-conf": "^0.5.0",
|
||||||
"archiver": "^3.0.0",
|
"archiver": "^3.0.0",
|
||||||
"async-iterator-to-stream": "^1.0.1",
|
"async-iterator-to-stream": "^1.0.1",
|
||||||
"base64url": "^3.0.0",
|
"base64url": "^3.0.0",
|
||||||
"bind-property-descriptor": "^1.0.0",
|
|
||||||
"blocked": "^1.2.1",
|
"blocked": "^1.2.1",
|
||||||
"bluebird": "^3.5.1",
|
"bluebird": "^3.5.1",
|
||||||
"body-parser": "^1.18.2",
|
"body-parser": "^1.18.2",
|
||||||
|
@ -1,13 +1,3 @@
|
|||||||
import { getBoundPropertyDescriptor } from 'bind-property-descriptor'
|
|
||||||
|
|
||||||
import { isArray, isFunction } from './utils'
|
|
||||||
|
|
||||||
// ===================================================================
|
|
||||||
|
|
||||||
const { defineProperties, getOwnPropertyDescriptor } = Object
|
|
||||||
|
|
||||||
// ===================================================================
|
|
||||||
|
|
||||||
// Debounce decorator for methods.
|
// Debounce decorator for methods.
|
||||||
//
|
//
|
||||||
// See: https://github.com/wycats/javascript-decorators
|
// See: https://github.com/wycats/javascript-decorators
|
||||||
@ -49,118 +39,3 @@ export const debounce = duration => (target, name, descriptor) => {
|
|||||||
descriptor.value = debounced
|
descriptor.value = debounced
|
||||||
return descriptor
|
return descriptor
|
||||||
}
|
}
|
||||||
|
|
||||||
// -------------------------------------------------------------------
|
|
||||||
|
|
||||||
const _ownKeys =
|
|
||||||
(typeof Reflect !== 'undefined' && Reflect.ownKeys) ||
|
|
||||||
(({ getOwnPropertyNames: names, getOwnPropertySymbols: symbols }) =>
|
|
||||||
symbols ? obj => names(obj).concat(symbols(obj)) : names)(Object)
|
|
||||||
|
|
||||||
const _isIgnoredProperty = name => name[0] === '_' || name === 'constructor'
|
|
||||||
|
|
||||||
const _IGNORED_STATIC_PROPERTIES = {
|
|
||||||
__proto__: null,
|
|
||||||
|
|
||||||
arguments: true,
|
|
||||||
caller: true,
|
|
||||||
length: true,
|
|
||||||
name: true,
|
|
||||||
prototype: true,
|
|
||||||
}
|
|
||||||
const _isIgnoredStaticProperty = name => _IGNORED_STATIC_PROPERTIES[name]
|
|
||||||
|
|
||||||
export const mixin = MixIns => Class => {
|
|
||||||
if (!isArray(MixIns)) {
|
|
||||||
MixIns = [MixIns]
|
|
||||||
}
|
|
||||||
|
|
||||||
const { name } = Class
|
|
||||||
|
|
||||||
// Copy properties of plain object mix-ins to the prototype.
|
|
||||||
{
|
|
||||||
const allMixIns = MixIns
|
|
||||||
MixIns = []
|
|
||||||
const { prototype } = Class
|
|
||||||
const descriptors = { __proto__: null }
|
|
||||||
for (const MixIn of allMixIns) {
|
|
||||||
if (isFunction(MixIn)) {
|
|
||||||
MixIns.push(MixIn)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const prop of _ownKeys(MixIn)) {
|
|
||||||
if (prop in prototype) {
|
|
||||||
throw new Error(`${name}#${prop} is already defined`)
|
|
||||||
}
|
|
||||||
|
|
||||||
;(descriptors[prop] = getOwnPropertyDescriptor(
|
|
||||||
MixIn,
|
|
||||||
prop
|
|
||||||
)).enumerable = false // Object methods are enumerable but class methods are not.
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineProperties(prototype, descriptors)
|
|
||||||
}
|
|
||||||
|
|
||||||
function Decorator (...args) {
|
|
||||||
const instance = new Class(...args)
|
|
||||||
|
|
||||||
for (const MixIn of MixIns) {
|
|
||||||
const { prototype } = MixIn
|
|
||||||
const mixinInstance = new MixIn(instance, ...args)
|
|
||||||
const descriptors = { __proto__: null }
|
|
||||||
for (const prop of _ownKeys(prototype)) {
|
|
||||||
if (_isIgnoredProperty(prop)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prop in instance) {
|
|
||||||
throw new Error(`${name}#${prop} is already defined`)
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptors[prop] = getBoundPropertyDescriptor(
|
|
||||||
prototype,
|
|
||||||
prop,
|
|
||||||
mixinInstance
|
|
||||||
)
|
|
||||||
}
|
|
||||||
defineProperties(instance, descriptors)
|
|
||||||
}
|
|
||||||
|
|
||||||
return instance
|
|
||||||
}
|
|
||||||
|
|
||||||
// Copy original and mixed-in static properties on Decorator class.
|
|
||||||
const descriptors = { __proto__: null }
|
|
||||||
for (const prop of _ownKeys(Class)) {
|
|
||||||
let descriptor
|
|
||||||
if (
|
|
||||||
!(
|
|
||||||
_isIgnoredStaticProperty(prop) &&
|
|
||||||
// if they already exist...
|
|
||||||
(descriptor = getOwnPropertyDescriptor(Decorator, prop)) &&
|
|
||||||
// and are not configurable.
|
|
||||||
!descriptor.configurable
|
|
||||||
)
|
|
||||||
) {
|
|
||||||
descriptors[prop] = getOwnPropertyDescriptor(Class, prop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (const MixIn of MixIns) {
|
|
||||||
for (const prop of _ownKeys(MixIn)) {
|
|
||||||
if (_isIgnoredStaticProperty(prop)) {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
if (prop in descriptors) {
|
|
||||||
throw new Error(`${name}.${prop} is already defined`)
|
|
||||||
}
|
|
||||||
|
|
||||||
descriptors[prop] = getOwnPropertyDescriptor(MixIn, prop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
defineProperties(Decorator, descriptors)
|
|
||||||
|
|
||||||
return Decorator
|
|
||||||
}
|
|
||||||
|
@ -2,6 +2,7 @@
|
|||||||
import concurrency from 'limit-concurrency-decorator'
|
import concurrency from 'limit-concurrency-decorator'
|
||||||
import deferrable from 'golike-defer'
|
import deferrable from 'golike-defer'
|
||||||
import fatfs from 'fatfs'
|
import fatfs from 'fatfs'
|
||||||
|
import mixin from '@xen-orchestra/mixin'
|
||||||
import synchronized from 'decorator-synchronized'
|
import synchronized from 'decorator-synchronized'
|
||||||
import tarStream from 'tar-stream'
|
import tarStream from 'tar-stream'
|
||||||
import vmdkToVhd from 'xo-vmdk-to-vhd'
|
import vmdkToVhd from 'xo-vmdk-to-vhd'
|
||||||
@ -32,7 +33,6 @@ import { satisfies as versionSatisfies } from 'semver'
|
|||||||
|
|
||||||
import createSizeStream from '../size-stream'
|
import createSizeStream from '../size-stream'
|
||||||
import fatfsBuffer, { init as fatfsBufferInit } from '../fatfs-buffer'
|
import fatfsBuffer, { init as fatfsBufferInit } from '../fatfs-buffer'
|
||||||
import { mixin } from '../decorators'
|
|
||||||
import {
|
import {
|
||||||
asyncMap,
|
asyncMap,
|
||||||
camelToSnakeCase,
|
camelToSnakeCase,
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
import XoCollection from 'xo-collection'
|
import XoCollection from 'xo-collection'
|
||||||
import XoUniqueIndex from 'xo-collection/unique-index'
|
import XoUniqueIndex from 'xo-collection/unique-index'
|
||||||
|
import mixin from '@xen-orchestra/mixin'
|
||||||
import { createClient as createRedisClient } from 'redis'
|
import { createClient as createRedisClient } from 'redis'
|
||||||
import { EventEmitter } from 'events'
|
import { EventEmitter } from 'events'
|
||||||
import { noSuchObject } from 'xo-common/api-errors'
|
import { noSuchObject } from 'xo-common/api-errors'
|
||||||
@ -16,7 +17,6 @@ import {
|
|||||||
|
|
||||||
import mixins from './xo-mixins'
|
import mixins from './xo-mixins'
|
||||||
import Connection from './connection'
|
import Connection from './connection'
|
||||||
import { mixin } from './decorators'
|
|
||||||
import { generateToken, noop } from './utils'
|
import { generateToken, noop } from './utils'
|
||||||
|
|
||||||
// ===================================================================
|
// ===================================================================
|
||||||
|
Loading…
Reference in New Issue
Block a user