feat(value-matcher): new package (#30)
This commit is contained in:
@@ -30,6 +30,7 @@
|
||||
"testRegex": "\\.spec\\.js$",
|
||||
"transform": {
|
||||
"complex-matcher/.+\\.jsx?$": "babel-7-jest",
|
||||
"value-matcher/.+\\.jsx?$": "babel-7-jest",
|
||||
"xo-cli/.+\\.jsx?$": "babel-7-jest",
|
||||
"\\.jsx?$": "babel-jest"
|
||||
}
|
||||
|
||||
27
packages/value-matcher/.babelrc.js
Normal file
27
packages/value-matcher/.babelrc.js
Normal file
@@ -0,0 +1,27 @@
|
||||
const { NODE_ENV = 'development' } = process.env
|
||||
const __PROD__ = NODE_ENV === 'production'
|
||||
const __TEST__ = NODE_ENV === 'test'
|
||||
|
||||
module.exports = {
|
||||
comments: !__PROD__,
|
||||
compact: __PROD__,
|
||||
ignore: __TEST__ ? undefined : [ /\.spec\.js$/ ],
|
||||
presets: [
|
||||
[
|
||||
'@babel/env',
|
||||
{
|
||||
debug: !__TEST__,
|
||||
loose: true,
|
||||
shippedProposals: true,
|
||||
targets: __PROD__
|
||||
? {
|
||||
browsers: '>2%',
|
||||
node: '4',
|
||||
}
|
||||
: { node: 'current' },
|
||||
useBuiltIns: 'usage',
|
||||
},
|
||||
],
|
||||
'@babel/flow',
|
||||
],
|
||||
}
|
||||
24
packages/value-matcher/.npmignore
Normal file
24
packages/value-matcher/.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__/
|
||||
66
packages/value-matcher/README.md
Normal file
66
packages/value-matcher/README.md
Normal file
@@ -0,0 +1,66 @@
|
||||
# value-matcher [](https://travis-ci.org/vatefr/xen-orchestra)
|
||||
|
||||
> ${pkg.description}
|
||||
|
||||
## Install
|
||||
|
||||
Installation of the [npm package](https://npmjs.org/package/value-matcher):
|
||||
|
||||
```
|
||||
> npm install --save value-matcher
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
```js
|
||||
import { createPredicate } from 'value-matcher'
|
||||
|
||||
[
|
||||
{ user: 'sam', age: 65, active: false },
|
||||
{ user: 'barney', age: 36, active: true },
|
||||
{ user: 'fred', age: 40, active: false },
|
||||
].filter(createPredicate({
|
||||
__or: [
|
||||
{ user: 'sam' },
|
||||
{ active: true },
|
||||
],
|
||||
}))
|
||||
// [
|
||||
// { user: 'sam', age: 65, active: false },
|
||||
// { user: 'barney', age: 36, active: true },
|
||||
// ]
|
||||
```
|
||||
|
||||
## 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](https://github.com/vatesfr/xo-web/issues)
|
||||
you've encountered;
|
||||
- fork and create a pull request.
|
||||
|
||||
## License
|
||||
|
||||
ISC © [Vates SAS](https://vates.fr)
|
||||
45
packages/value-matcher/package.json
Normal file
45
packages/value-matcher/package.json
Normal file
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"private": true,
|
||||
"name": "value-matcher",
|
||||
"version": "0.0.0",
|
||||
"license": "ISC",
|
||||
"description": "",
|
||||
"keywords": [],
|
||||
"homepage": "https://github.com/vatesfr/xen-orchestra/tree/master/packages/value-matcher",
|
||||
"bugs": "https://github.com/vatesfr/xo-web/issues",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/vatesfr/xen-orchestra.git"
|
||||
},
|
||||
"author": {
|
||||
"name": "Julien Fontanet",
|
||||
"email": "julien.fontanet@isonoe.net"
|
||||
},
|
||||
"preferGlobal": false,
|
||||
"main": "dist/",
|
||||
"bin": {},
|
||||
"files": [
|
||||
"dist/"
|
||||
],
|
||||
"engines": {
|
||||
"node": ">=4"
|
||||
},
|
||||
"dependencies": {
|
||||
"@babel/polyfill": "^7.0.0-beta.36"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.0.0-beta.36",
|
||||
"@babel/core": "^7.0.0-beta.36",
|
||||
"@babel/preset-env": "^7.0.0-beta.36",
|
||||
"@babel/preset-flow": "^7.0.0-beta.36",
|
||||
"cross-env": "^5.1.3",
|
||||
"rimraf": "^2.6.2"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production babel --source-maps --out-dir=dist/ src/",
|
||||
"dev": "cross-env NODE_ENV=development babel --watch --source-maps --out-dir=dist/ src/",
|
||||
"prebuild": "rimraf dist/",
|
||||
"predev": "yarn run prebuild",
|
||||
"prepublishOnly": "yarn run build"
|
||||
}
|
||||
}
|
||||
63
packages/value-matcher/src/index.js
Normal file
63
packages/value-matcher/src/index.js
Normal file
@@ -0,0 +1,63 @@
|
||||
// @flow
|
||||
|
||||
// eslint-disable-next-line no-use-before-define
|
||||
export type Pattern = OrPattern | NotPattern | ObjectPattern | ArrayPattern | ValuePattern
|
||||
|
||||
// one of the pattern must match
|
||||
type OrPattern = {| __or: Array<Pattern> |}
|
||||
|
||||
// the pattern must not match
|
||||
type NotPattern = {| __not: Pattern |}
|
||||
|
||||
// value is an object with properties matching the patterns
|
||||
type ObjectPattern = { [string]: Pattern }
|
||||
|
||||
// value is an array and each patterns must match a different item
|
||||
type ArrayPattern = Array<Pattern>
|
||||
|
||||
// value equals the pattern
|
||||
type ValuePattern = bool | number | string
|
||||
|
||||
const match = (pattern: Pattern, value: any) => {
|
||||
if (Array.isArray(pattern)) {
|
||||
return Array.isArray(value) && pattern.every((subpattern, i) =>
|
||||
// FIXME: subpatterns should match different subvalues
|
||||
value.some(subvalue => match(subpattern, subvalue))
|
||||
)
|
||||
}
|
||||
|
||||
if (pattern !== null && typeof pattern === 'object') {
|
||||
const keys = Object.keys(pattern)
|
||||
const { length } = keys
|
||||
|
||||
if (length === 1) {
|
||||
const [ key ] = keys
|
||||
if (key === '__or') {
|
||||
const orPattern: OrPattern = (pattern: any)
|
||||
return orPattern.__or.some(subpattern => match(subpattern, value))
|
||||
}
|
||||
if (key === '__not') {
|
||||
const notPattern: NotPattern = (pattern: any)
|
||||
return !match(notPattern.__not, value)
|
||||
}
|
||||
}
|
||||
|
||||
if (value === null || typeof value !== 'object') {
|
||||
return false
|
||||
}
|
||||
|
||||
const objectPattern: ObjectPattern = (pattern: any)
|
||||
for (let i = 0; i < length; ++i) {
|
||||
const key = keys[i]
|
||||
const subvalue = value[key]
|
||||
if (subvalue === undefined || !match(objectPattern[key], subvalue)) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
return pattern === value
|
||||
}
|
||||
|
||||
export const createPredicate = (pattern: Pattern) => (value: any) => match(pattern, value)
|
||||
Reference in New Issue
Block a user