Skip to content

Commit

Permalink
Merge pull request #6 from orioro/async
Browse files Browse the repository at this point in the history
Async
  • Loading branch information
simonfan authored Mar 3, 2021
2 parents 4290c29 + 8bd0db2 commit bde0238
Show file tree
Hide file tree
Showing 33 changed files with 808 additions and 558 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,4 @@ node_modules
*.bundle.js
dist
coverage
tmp
4 changes: 1 addition & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -42,12 +42,11 @@
"@babel/core": "^7.11.5",
"@babel/preset-env": "^7.11.5",
"@babel/preset-typescript": "^7.10.4",
"@orioro/jest-util": "^1.2.0",
"@orioro/jest-util": "^1.3.0",
"@orioro/readme": "^1.0.1",
"@rollup/plugin-babel": "^5.2.0",
"@rollup/plugin-commonjs": "^15.0.0",
"@rollup/plugin-node-resolve": "^9.0.0",
"@semantic-release/changelog": "^5.0.1",
"@types/jest": "^26.0.14",
"@typescript-eslint/eslint-plugin": "^4.15.0",
"@typescript-eslint/parser": "^4.15.0",
Expand Down Expand Up @@ -77,7 +76,6 @@
"plugins": [
"@semantic-release/commit-analyzer",
"@semantic-release/release-notes-generator",
"@semantic-release/changelog",
"@semantic-release/npm",
"@semantic-release/github"
]
Expand Down
6 changes: 5 additions & 1 deletion src/__snapshots__/index.spec.ts.snap
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,10 @@ Array [
"isExpression",
"evaluate",
"evaluateTyped",
"interpreter",
"evaluateTypedAsync",
"syncInterpreter",
"syncInterpreterList",
"asyncInterpreter",
"asyncInterpreterList",
]
`;
82 changes: 82 additions & 0 deletions src/async.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import { testCases, asyncResult } from '@orioro/jest-util'
// import { validateType } from '@orioro/typing'

import { ALL_EXPRESSIONS } from './'

import { asyncInterpreterList } from './interpreter'

import { evaluate } from './evaluate'

const wait = (ms, result) =>
new Promise((resolve) => setTimeout(resolve.bind(null, result), ms))

const $asyncLoadStr = [() => wait(100, 'async-str'), []]
const $asyncLoadNum = [() => wait(100, 9), []]
const $asyncLoadArr = [() => wait(100, ['str-1', 'str-2', 'str-3']), []]
const $asyncLoadObj = [
() =>
wait(100, {
key1: 'value1',
key2: 'value2',
}),
[],
]
const $asyncLoadTrue = [() => wait(100, true), []]
const $asyncLoadFalse = [() => wait(100, false), []]

const interpreters = asyncInterpreterList({
...ALL_EXPRESSIONS,
$asyncLoadStr,
$asyncLoadNum,
$asyncLoadArr,
$asyncLoadObj,
$asyncLoadTrue,
$asyncLoadFalse,
})

describe('async - immediate async expression', () => {
testCases(
[
['$asyncLoadStr', asyncResult('async-str')],
['$asyncLoadNum', asyncResult(9)],
['$asyncLoadArr', asyncResult(['str-1', 'str-2', 'str-3'])],
['$asyncLoadObj', asyncResult({ key1: 'value1', key2: 'value2' })],
['$asyncLoadTrue', asyncResult(true)],
['$asyncLoadFalse', asyncResult(false)],
],
(expression) =>
evaluate({ interpreters, scope: { $$VALUE: null } }, [expression])
)
})

describe('async - nested async expression', () => {
test('simple scenario - string concat', () => {
return expect(
evaluate(
{
interpreters,
scope: {
$$VALUE: 'value-',
},
},
['$stringConcat', ['$asyncLoadStr']]
)
).resolves.toEqual('value-async-str')
})
})

describe('async - syncronous expressions only get converted to async as well', () => {
test('simple scenario - string concat', () => {
return expect(
evaluate(
{
interpreters,
scope: {
$$VALUE: 'value-',
},
},
['$stringConcat', 'sync-value']
)
).resolves.toEqual('value-sync-value')
})
})
2 changes: 1 addition & 1 deletion src/expression.spec.ts → src/evaluate.spec.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { evaluateTyped } from './expression'
import { evaluateTyped } from './evaluate'

describe('evaluateTyped(expectedTypes, context, value)', () => {
test('simple type - example: number', () => {
Expand Down
103 changes: 103 additions & 0 deletions src/evaluate.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { validateType } from '@orioro/typing'

import {
Expression,
ExpressionInterpreterList,
EvaluationContext,
} from './types'

/**
* @function isExpression
* @param {ExpressionInterpreterList}
*/
export const isExpression = (
interpreters: ExpressionInterpreterList,
candidateExpression: any // eslint-disable-line @typescript-eslint/explicit-module-boundary-types
): boolean =>
Array.isArray(candidateExpression) &&
typeof interpreters[candidateExpression[0]] === 'function'

const _maybeExpression = (value) =>
Array.isArray(value) &&
typeof value[0] === 'string' &&
value[0].startsWith('$')

const _ellipsis = (str, maxlen = 50) =>
str.length > maxlen ? str.substr(0, maxlen - 1).concat('...') : str

const _evaluateDev = (
context: EvaluationContext,
expOrValue: Expression | any
): any => {
if (
!isExpression(context.interpreters, expOrValue) &&
_maybeExpression(expOrValue)
) {
console.warn(
`Possible missing expression error: ${_ellipsis(
JSON.stringify(expOrValue)
)}. No interpreter was found for '${expOrValue[0]}'`
)
}

return _evaluate(context, expOrValue)
}

const _evaluate = (
context: EvaluationContext,
expOrValue: Expression | any
): any => {
if (!isExpression(context.interpreters, expOrValue)) {
return expOrValue
}

const [interpreterId, ...interpreterArgs] = expOrValue
const interpreter = context.interpreters[interpreterId]

return interpreter(context, ...interpreterArgs)
}

/**
* @function evaluate
* @param {EvaluationContext} context
* @param {Expression | *} expOrValue
* @returns {*}
*/
export const evaluate =
process && process.env && process.env.NODE_ENV !== 'production'
? _evaluateDev
: _evaluate

/**
* @function evaluateTyped
* @param {String | string[]} expectedTypes
* @param {EvaluationContext} context
* @param {Expression | any} expOrValue
* @returns {*}
*/
export const evaluateTyped = (
expectedTypes: string | string[],
context: EvaluationContext,
expOrValue: Expression | any
): any => {
const value = evaluate(context, expOrValue)
validateType(expectedTypes, value)
return value
}

/**
* @function evaluateTypedAsync
* @param {String | string[]} expectedTypes
* @param {EvaluationContext} context
* @param {Expression | any} expOrValue
* @returns {Promise<*>}
*/
export const evaluateTypedAsync = (
expectedTypes: string | string[],
context: EvaluationContext,
expOrValue: Expression | any
): Promise<any> =>
Promise.resolve(evaluate(context, expOrValue)).then((value) => {
validateType(expectedTypes, value)
return value
})
Loading

0 comments on commit bde0238

Please sign in to comment.