Closed jcalfee closed 1 year ago
Just looked at the timm code and a work around should be:
const additionalJsContext = {
key: new Proxy({}, {
set(obj, prop, value) {
debug('set', {prop, value})
obj[prop] = value
return true
},
get(obj, prop) {
const value = obj[prop]
debug('get', {prop, value})
return value === undefined ? null : value
}
})
}
Where the key is not present in data. (and is not code nor result)
[edit] Sorry I just got what you want...
I might have a solution for that... But that will not work in IE.
OK I wrote some prototype code:
Let oldMerge be the current merge function and replace it with this new one. Did not test this code in any way so expect bugs!
const supportsProxy = 'Proxy' in window
let oldMerge: (...args: any[]) => any
function merge(...objects: any[]): any {
if(objects.length === 1) return objects[0]
if(!supportsProxy) return oldMerge(...objects)
// Even if non root objects are Proxies run proxy version as this makes it posible to have proxies as values.
let first = true
const ext = {}
return new Proxy({}, {
apply() {
throw new Error("You can't call the merge Proxy");
},
construct() {
throw new Error("You can't call new on the merge Proxy");
},
getPrototypeOf() {
return null
},
get(_, key) {
const obj = objects.filter(v=>v instanceof Proxy || key in v)
if(!obj.some(v=>v instanceof Proxy)) return oldMerge(...obj.map(v=>Reflect.get(v, key, v)))
return merge(...obj.map(v=>Reflect.get(v, key, v)))
},
getOwnPropertyDescriptor(_, p: string | symbol) {
const obj = objects.filter(v=>p in v)
if(obj.length !== 1) return
return Reflect.getOwnPropertyDescriptor(obj, p)
},
has(_, p: string | symbol) {
return objects.some(v=>Reflect.has(v, p))
},
ownKeys() {
return objects.flatMap(v => Reflect.ownKeys(v))
},
set(_, p: string | symbol, value: any) {
const obj = objects.filter(v=>v instanceof Proxy || p in v)
for (let i = 0; i < obj.length; i++) {
const r = Reflect.set(obj[i], p, value, obj[i])
if(r) return true
}
if(first) objects.push(ext)
first = false
return Reflect.set(ext, p, value, ext)
},
defineProperty(_, p: string | symbol, attributes: PropertyDescriptor) {
if(first) objects.push(ext)
first = false
return Reflect.defineProperty(ext, p, attributes)
},
deleteProperty(_, p: string | symbol) {
return objects.some(v => Reflect.deleteProperty(v, p))
},
setPrototypeOf() {
return false
},
preventExtensions() {
return false
},
isExtensible() {
return true
}
})
}
[EDIT] added all the other traps
Wrapping it in a key works. That opens up the option to report missing keys in the template or run more dynamic async code. So that is a really good work around. Of course means the prefix will need to be used in the templates.
What will delete do? delete it from all current objects?
In the original merge
returns an immutable object
That new proxy looks interesting. I did this:
function oldMerge(...args: any[]): any {
return merge(args)
}
function proxyMerge(...objects: any[]): any {
//...
And adjusted the recursive call in "get" and plugged in proxyMerge
..
There is an error here:
var obj = objects.filter(function (v) { return v instanceof Proxy || key in v; });
^
TypeError: Function has non-object prototype 'undefined' in instanceof check
Accounting for undefined did not work:
var obj = objects.filter(function (v) { return (v === undefined ? false : v instanceof Proxy || key in v); });
^
And adjusted the recursive call in "get" and plugged in
proxyMerge
..
That is not needed (and will break).
The error should be gone with that change and if not replace v instanceof Proxy
with (typeof v === 'object' && v instanceof Proxy)
.
Yea, I realized recursion didn't make since. I'm still not sure what get should contain though.
maybe
get(_, key) {
const obj = objects.filter(v=>v instanceof Proxy || key in v)
if(obj.length === 0) return
if(obj.lenght===1) return Reflect.get(obj[0], key, obj[0])
// Dont have to deal with Proxy
if(!obj.some(v=>v instanceof Proxy)) return oldMerge(...obj.map(v=>Reflect.get(v, key, v)))
// there is a proxy so get them all and merge them (this allows Proxies to return Proxies or objects with values with proxies)
return merge(...obj.map(v=>Reflect.get(v, key, v)))
},
I tested v.__isProxy !== undefined
ref and it works when I replace the two v instanceof Proxy
checks . It errors err: Error: TypeError: eval is not a function
. Probably this line:
https://github.com/guigrpa/docx-templates/blob/28496607960efab8619ca006256d1d50fe5c0c7e/src/jsSandbox.ts#L49
Closing this because it seems resolved.
Is it possible for additionalJsContext to capture a generic getter or setter from a proxy?
I'm providing:
Then merge is used in this example [ref]:
The generic "get" and "set" are not being merged into the sandbox object. If I put static properties in the 1st argument to Proxy it works or if I test my proxy alone
additionalJsContext.anyprop === null
it works. I'm not concerned about Proxy immutability, I can handle that with theset
.I'm have asked on the timm project too: https://github.com/guigrpa/timm/issues/52
This may be better handled here. Looks like using
data
instead will not help either. I'm also after the async support in additionalJsContext. Also, if you have a suggestion I don't mind forking making changes and testing. I anticipate that I'm going to need to do this anyways.