Skip to content

Commit

Permalink
♻️ improves merge proxies (#3722)
Browse files Browse the repository at this point in the history
* 🐛 Allows JSON stringifying $data

* ♻️ Uses Reflect

* ⚡ Shortcircuits on unscopables

* ♻️ Extract proxyTraps

* ✅ Uses legacy syntax

* 🔇 removes log

Co-authored-by: Christian Taylor <christianbtaylor@gmail.com>

* Tweak styling

---------

Co-authored-by: Christian Taylor <christianbtaylor@gmail.com>
Co-authored-by: Caleb Porzio <calebporzio@gmail.com>
  • Loading branch information
3 people authored Sep 11, 2023
1 parent 87d65fc commit e1a6bf0
Showing 1 changed file with 50 additions and 61 deletions.
111 changes: 50 additions & 61 deletions packages/alpinejs/src/scope.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,65 +33,54 @@ export function closestDataProxy(el) {
return mergeProxies(closestDataStack(el))
}

export function mergeProxies(objects) {
let thisProxy = new Proxy({}, {
ownKeys: () => {
return Array.from(new Set(objects.flatMap(i => Object.keys(i))))
},

has: (target, name) => {
return objects.some(obj => obj.hasOwnProperty(name))
},

get: (target, name) => {
return (objects.find(obj => {
if (obj.hasOwnProperty(name)) {
let descriptor = Object.getOwnPropertyDescriptor(obj, name)

// If we already bound this getter, don't rebind.
if ((descriptor.get && descriptor.get._x_alreadyBound) || (descriptor.set && descriptor.set._x_alreadyBound)) {
return true
}

// Properly bind getters and setters to this wrapper Proxy.
if ((descriptor.get || descriptor.set) && descriptor.enumerable) {
// Only bind user-defined getters, not our magic properties.
let getter = descriptor.get
let setter = descriptor.set
let property = descriptor

getter = getter && getter.bind(thisProxy)
setter = setter && setter.bind(thisProxy)

if (getter) getter._x_alreadyBound = true
if (setter) setter._x_alreadyBound = true

Object.defineProperty(obj, name, {
...property,
get: getter,
set: setter,
})
}

return true
}

return false
}) || {})[name]
},

set: (target, name, value) => {
let closestObjectWithKey = objects.find(obj => obj.hasOwnProperty(name))

if (closestObjectWithKey) {
closestObjectWithKey[name] = value
} else {
objects[objects.length - 1][name] = value
}

return true
},
})

return thisProxy
export function mergeProxies (objects) {
return new Proxy({ objects }, mergeProxyTrap);
}

let mergeProxyTrap = {
ownKeys({ objects }) {
return Array.from(
new Set(objects.flatMap((i) => Object.keys(i)))
)
},

has({ objects }, name) {
if (name == Symbol.unscopables) return false;

return objects.some((obj) =>
Object.prototype.hasOwnProperty.call(obj, name)
);
},

get({ objects }, name, thisProxy) {
if (name == "toJSON") return collapseProxies

return Reflect.get(
objects.find((obj) =>
Object.prototype.hasOwnProperty.call(obj, name)
) || {},
name,
thisProxy
)
},

set({ objects }, name, value) {
return Reflect.set(
objects.find((obj) =>
Object.prototype.hasOwnProperty.call(obj, name)
) || objects[objects.length-1],
name,
value
)
},
}

function collapseProxies() {
let keys = Reflect.ownKeys(this)

return keys.reduce((acc, key) => {
acc[key] = Reflect.get(this, key)

return acc;
}, {})
}

0 comments on commit e1a6bf0

Please sign in to comment.