Skip to content

Commit 1639613

Browse files
committed
Add simple js expression model
1 parent db8b0cc commit 1639613

File tree

7 files changed

+95
-6
lines changed

7 files changed

+95
-6
lines changed

lib/constant.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// Public: A constant value like a "string", number (1, 3.5), or boolean (true, false).
2+
//
3+
// Implements the same interface as Expression
4+
export class Constant {
5+
constructor (value) {
6+
this.value = value
7+
}
8+
9+
get args () {
10+
return [this.value]
11+
}
12+
}

lib/explorer.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import schemas, { BaseURI } from '../schemas'
12
import pointer from 'json-pointer'
23

34
export class Explorer {
@@ -40,3 +41,5 @@ export class Explorer {
4041
return this.get('#/definitions/function/properties')
4142
}
4243
}
44+
45+
export default new Explorer(schemas, BaseURI)

lib/expression.js

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
import { v4 as uuidv4 } from 'uuid'
2+
import { Constant } from './constant'
3+
import explorer from './explorer'
4+
5+
// Simple model to transform this: `{ All: [{ Boolean: [true] }]`
6+
// into this: `{ id: uuidv4(), name: 'All', args: [{ id: uuidv4(), name: 'Boolean', args: [true] }] }`
7+
export class Expression {
8+
static build (expression) {
9+
if (expression instanceof Expression || expression instanceof Constant) {
10+
return expression
11+
}
12+
13+
if (typeof expression === 'object') {
14+
const name = Object.keys(expression)[0]
15+
const args = expression[name].map(Expression.build)
16+
return new Expression({ name, args })
17+
} else {
18+
return new Constant(expression)
19+
}
20+
}
21+
22+
constructor ({ name, args, id = uuidv4() }) {
23+
Object.assign(this, { name, args, id })
24+
}
25+
26+
clone ({ id = this.id, name = this.name, args = this.args } = {}) {
27+
return new Expression({ id, name, args: args.map(Expression.build) })
28+
}
29+
30+
get value () {
31+
return { [this.name]: this.args.map(arg => arg.value) }
32+
}
33+
34+
get schema () {
35+
return explorer.functions[this.name]
36+
}
37+
}

lib/index.js

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
1-
import schemas, { BaseURI } from '../schemas'
2-
import { Explorer } from './explorer'
3-
4-
export const explorer = new Explorer(schemas, BaseURI)
5-
export { schemas }
1+
export { default as schemas, BaseURI } from '../schemas'
62
export { default as examples } from '../examples'
3+
export { default as explorer } from './explorer'
74
export { default as validate } from './validate'
5+
export { Expression } from './expression'
6+
export { Constant } from './constant'

package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
"dependencies": {
2222
"ajv": "^8.12.0",
2323
"ajv-formats": "^2.1.1",
24-
"json-pointer": "^0.6.2"
24+
"json-pointer": "^0.6.2",
25+
"uuid": "^9.0.0"
2526
},
2627
"devDependencies": {
2728
"standard": "^17.0.0",

test/expression.test.js

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { describe, test, expect } from 'vitest'
2+
import { Expression, Constant } from '../lib'
3+
4+
describe('Expression', () => {
5+
describe('build', () => {
6+
test('builds an expression from an object', () => {
7+
const expression = Expression.build({ All: [true] })
8+
expect(expression.name).toEqual('All')
9+
expect(expression.args[0]).toBeInstanceOf(Constant)
10+
expect(expression.args[0].value).toEqual(true)
11+
expect(expression.value).toEqual({ All: [true] })
12+
})
13+
})
14+
15+
describe('clone', () => {
16+
test('returns new expression', () => {
17+
const expression = Expression.build({ All: [true] })
18+
const clone = expression.clone()
19+
expect(clone).not.toBe(expression)
20+
expect(clone.name).toEqual(expression.name)
21+
expect(clone.args).toEqual(expression.args)
22+
expect(clone.id).toEqual(expression.id)
23+
})
24+
25+
test('builds args', () => {
26+
const expression = Expression.build({ All: [] })
27+
const clone = expression.clone({ args: [true] })
28+
expect(clone.args[0]).toBeInstanceOf(Constant)
29+
expect(clone.value).toEqual({ All: [true] })
30+
})
31+
})
32+
})

yarn.lock

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2005,6 +2005,11 @@ uri-js@^4.2.2:
20052005
dependencies:
20062006
punycode "^2.1.0"
20072007

2008+
uuid@^9.0.0:
2009+
version "9.0.0"
2010+
resolved "https://registry.yarnpkg.com/uuid/-/uuid-9.0.0.tgz#592f550650024a38ceb0c562f2f6aa435761efb5"
2011+
integrity sha512-MXcSTerfPa4uqyzStbRoTgt5XIe3x5+42+q1sDuy3R5MDk66URdLMOZe5aPX/SQd+kuYAh0FdP/pO28IkQyTeg==
2012+
20082013
20092014
version "0.29.8"
20102015
resolved "https://registry.yarnpkg.com/vite-node/-/vite-node-0.29.8.tgz#6a1c9d4fb31e7b4e0f825d3a37abe3404e52bd8e"

0 commit comments

Comments
 (0)