Skip to content

Commit aacfa71

Browse files
committed
feat(hide, expose): allow the user to hide/expose globals for the compiled expressions and codes
1 parent 0189b9c commit aacfa71

File tree

3 files changed

+82
-4
lines changed

3 files changed

+82
-4
lines changed

README.md

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -76,6 +76,32 @@ context.item = {name: 'item name'}
7676
result = expression(context) // result is 'item name'
7777
```
7878

79+
### compiler.expose(String)
80+
81+
Use this method to expose globals to the compiler. Non of the globals are exposed by default.
82+
83+
```js
84+
const code = compiler.compileCode('console.log(message)')
85+
compiler.expose('console')
86+
code({message: 'Hello World'}) // logs 'Hello World' to the console
87+
```
88+
89+
Context variables are always favored over global ones, when both are present (with the same name).
90+
91+
### compiler.hide(String)
92+
93+
Use this method to hide exposed globals from the compiler.
94+
95+
```js
96+
const code = compiler.compileCode('console.log(message)')
97+
compiler.expose('console')
98+
code({message: 'Hello World'}) // logs 'Hello World' to the console
99+
compiler.hide('console')
100+
code({message: 'Hello World'}) // throws an error, console is undefined
101+
```
102+
103+
Context variables are always favored over global ones, when both are present (with the same name).
104+
79105
## Example
80106

81107
```js

compiler.js

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,9 @@
22

33
module.exports = {
44
compileCode,
5-
compileExpression
5+
compileExpression,
6+
expose,
7+
hide
68
}
79

810
let globalObj
@@ -15,6 +17,7 @@ globalObj.$nxCompileCreateBackup = createBackup
1517
const proxies = new WeakMap()
1618
const expressionCache = new Map()
1719
const codeCache = new Map()
20+
const exposedGlobals = new Set()
1821
const handlers = {has}
1922

2023
function compileExpression (src) {
@@ -39,7 +42,7 @@ function compileCode (src) {
3942
}
4043
let code = codeCache.get(src)
4144
if (!code) {
42-
code = new Function('context, tempVars', // eslint-disable-line
45+
code = new Function('context', 'tempVars', // eslint-disable-line
4346
`const backup = $nxCompileCreateBackup(context, tempVars)
4447
Object.assign(context, tempVars)
4548
const sandbox = $nxCompileToSandbox(context)
@@ -53,6 +56,20 @@ function compileCode (src) {
5356
return code
5457
}
5558

59+
function expose (globalName) {
60+
if (typeof globalName !== 'string') {
61+
throw new TypeError('first argument must be a string')
62+
}
63+
exposedGlobals.add(globalName)
64+
}
65+
66+
function hide (globalName) {
67+
if (typeof globalName !== 'string') {
68+
throw new TypeError('first argument must be a string')
69+
}
70+
exposedGlobals.delete(globalName)
71+
}
72+
5673
function toSandbox (obj) {
5774
if (typeof obj !== 'object') {
5875
throw new TypeError('first argument must be an object')
@@ -75,6 +92,6 @@ function createBackup (context, tempVars) {
7592
}
7693
}
7794

78-
function has () {
79-
return true
95+
function has (target, key) {
96+
return exposedGlobals.has(key) ? Reflect.has(target, key) : true
8097
}

compiler.test.js

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,4 +79,39 @@ describe('nx-compile', () => {
7979
expect(context.tempVar).to.be.undefined
8080
})
8181
})
82+
83+
describe('expose', () => {
84+
it('should throw a TypeError on non string source argument', () => {
85+
expect(() => compiler.expose({})).to.throw(TypeError)
86+
expect(() => compiler.expose(undefined)).to.throw(TypeError)
87+
expect(() => compiler.expose(12)).to.throw(TypeError)
88+
})
89+
90+
it('should expose the passed global', () => {
91+
const expression = compiler.compileExpression('console')
92+
expect(expression({})).to.be.undefined
93+
compiler.expose('console')
94+
expect(expression({})).to.equal(console)
95+
})
96+
97+
it('should favour sandbox props over exposed globals', () => {
98+
const expression = compiler.compileExpression('console')
99+
expect(expression({ console: 'prop' })).to.equal('prop')
100+
})
101+
})
102+
103+
describe('hide', () => {
104+
it('should throw a TypeError on non string source argument', () => {
105+
expect(() => compiler.hide({})).to.throw(TypeError)
106+
expect(() => compiler.hide(undefined)).to.throw(TypeError)
107+
expect(() => compiler.hide(12)).to.throw(TypeError)
108+
})
109+
110+
it('should hide exposed globals', () => {
111+
const expression = compiler.compileExpression('console')
112+
expect(expression({})).to.equal(console)
113+
compiler.hide('console')
114+
expect(expression({})).to.be.undefined
115+
})
116+
})
82117
})

0 commit comments

Comments
 (0)