Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

(Improve extensibility) Export injectMagics, remove dontAutoEvaluateFunctions #4309

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/alpinejs/src/alpine.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { mapAttributes, directive, setPrefix as prefix, prefix as prefixed } fro
import { start, addRootSelector, addInitSelector, closestRoot, findClosest, initTree, destroyTree, interceptInit } from './lifecycle'
import { onElRemoved, onAttributeRemoved, onAttributesAdded, mutateDom, deferMutations, flushAndStopDeferringMutations, startObservingMutations, stopObservingMutations } from './mutation'
import { mergeProxies, closestDataStack, addScopeToNode, scope as $data } from './scope'
import { setEvaluator, evaluate, evaluateLater, dontAutoEvaluateFunctions } from './evaluator'
import { setEvaluator, evaluate, evaluateLater } from './evaluator'
import { transition } from './directives/x-transition'
import { clone, cloneNode, skipDuringClone, onlyDuringClone, interceptClone } from './clone'
import { injectMagics, magic } from './magics'
import { interceptor } from './interceptor'
import { getBinding as bound, extractProp } from './utils/bind'
import { debounce } from './utils/debounce'
Expand All @@ -15,7 +16,6 @@ import { entangle } from './entangle'
import { nextTick } from './nextTick'
import { walk } from './utils/walk'
import { plugin } from './plugin'
import { magic } from './magics'
import { store } from './store'
import { bind } from './binds'
import { data } from './datas'
Expand All @@ -27,7 +27,6 @@ let Alpine = {
get raw() { return raw },
version: ALPINE_VERSION,
flushAndStopDeferringMutations,
dontAutoEvaluateFunctions,
disableEffectScheduling,
startObservingMutations,
stopObservingMutations,
Expand All @@ -46,6 +45,7 @@ let Alpine = {
evaluateLater,
interceptInit,
setEvaluator,
injectMagics,
mergeProxies,
extractProp,
findClosest,
Expand Down
42 changes: 12 additions & 30 deletions packages/alpinejs/src/evaluator.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,6 @@ import { closestDataStack, mergeProxies } from './scope'
import { injectMagics } from './magics'
import { tryCatch, handleError } from './utils/error'

let shouldAutoEvaluateFunctions = true

export function dontAutoEvaluateFunctions(callback) {
let cache = shouldAutoEvaluateFunctions

shouldAutoEvaluateFunctions = false

let result = callback()

shouldAutoEvaluateFunctions = cache

return result
}

export function evaluate(el, expression, extras = {}) {
let result

Expand Down Expand Up @@ -49,10 +35,10 @@ export function normalEvaluator(el, expression) {
}

export function generateEvaluatorFromFunction(dataStack, func) {
return (receiver = () => {}, { scope = {}, params = [] } = {}) => {
return (receiver = () => {}, { scope = {}, params = [], autoEvaluateFunctions = true } = {}) => {
let result = func.apply(mergeProxies([scope, ...dataStack]), params)

runIfTypeOfFunction(receiver, result)
handleEvalResult(autoEvaluateFunctions, receiver, result)
}
}

Expand Down Expand Up @@ -103,7 +89,7 @@ function generateFunctionFromString(expression, el) {
function generateEvaluatorFromString(dataStack, expression, el) {
let func = generateFunctionFromString(expression, el)

return (receiver = () => {}, { scope = {}, params = [] } = {}) => {
return (receiver = () => {}, { scope = {}, params = [], autoEvaluateFunctions = true } = {}) => {
func.result = undefined
func.finished = false

Expand All @@ -117,7 +103,7 @@ function generateEvaluatorFromString(dataStack, expression, el) {
// Check if the function ran synchronously,
if (func.finished) {
// Return the immediate result.
runIfTypeOfFunction(receiver, func.result, completeScope, params, el)
handleEvalResult(autoEvaluateFunctions, receiver, func.result, completeScope, params, el)
// Once the function has run, we clear func.result so we don't create
// memory leaks. func is stored in the evaluatorMemo and every time
// it runs, it assigns the evaluated expression to result which could
Expand All @@ -126,26 +112,22 @@ function generateEvaluatorFromString(dataStack, expression, el) {
} else {
// If not, return the result when the promise resolves.
promise.then(result => {
runIfTypeOfFunction(receiver, result, completeScope, params, el)
handleEvalResult(autoEvaluateFunctions, receiver, result, completeScope, params, el)
}).catch( error => handleError( error, el, expression ) )
.finally( () => func.result = undefined )
}
}
}
}

export function runIfTypeOfFunction(receiver, value, scope, params, el) {
if (shouldAutoEvaluateFunctions && typeof value === 'function') {
let result = value.apply(scope, params)
export function handleEvalResult(autoEvaluateFunctions, receiver, value, scope, params, el) {
const shouldCallFunc = autoEvaluateFunctions && typeof value === 'function'

if (result instanceof Promise) {
result.then(i => runIfTypeOfFunction(receiver, i, scope, params)).catch( error => handleError( error, el, value ) )
} else {
receiver(result)
}
} else if (typeof value === 'object' && value instanceof Promise) {
value.then(i => receiver(i))
const result = shouldCallFunc ? value.apply(scope, params) : value

if (result instanceof Promise) {
result.then(i => receiver(i)).catch( error => handleError( error, el, value ) )
} else {
receiver(value)
receiver(result)
}
}
6 changes: 2 additions & 4 deletions packages/alpinejs/src/utils/bind.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { dontAutoEvaluateFunctions, evaluate } from '../evaluator'
import { evaluate } from '../evaluator'
import { reactive } from '../reactivity'
import { setClasses } from './classes'
import { setStyles } from './styles'
Expand Down Expand Up @@ -180,9 +180,7 @@ export function extractProp(el, name, fallback, extract = true) {

binding.extract = extract

return dontAutoEvaluateFunctions(() => {
return evaluate(el, binding.expression)
})
return evaluate(el, binding.expression, { autoEvaluateFunctions: false })
}

return getAttributeBinding(el, name, fallback)
Expand Down
6 changes: 3 additions & 3 deletions packages/csp/src/evaluator.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { generateEvaluatorFromFunction, runIfTypeOfFunction } from 'alpinejs/src/evaluator'
import { generateEvaluatorFromFunction, handleEvalResult } from 'alpinejs/src/evaluator'
import { closestDataStack, mergeProxies } from 'alpinejs/src/scope'
import { tryCatch } from 'alpinejs/src/utils/error'
import { injectMagics } from 'alpinejs/src/magics'
Expand All @@ -25,7 +25,7 @@ function generateDataStack(el) {
}

function generateEvaluator(el, expression, dataStack) {
return (receiver = () => {}, { scope = {}, params = [] } = {}) => {
return (receiver = () => {}, { scope = {}, params = [], autoEvaluateFunctions = true } = {}) => {
let completeScope = mergeProxies([scope, ...dataStack])

let evaluatedExpression = expression.split('.').reduce(
Expand All @@ -39,7 +39,7 @@ function generateEvaluator(el, expression, dataStack) {
completeScope,
);

runIfTypeOfFunction(receiver, evaluatedExpression, completeScope, params)
handleEvalResult(autoEvaluateFunctions, receiver, evaluatedExpression, completeScope, params)
}
}

Expand Down
11 changes: 6 additions & 5 deletions packages/mask/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,14 +16,15 @@ export default function (Alpine) {

// We need to prevent "auto-evaluation" of functions like
// x-on expressions do so that we can use them as mask functions.
Alpine.dontAutoEvaluateFunctions(() => {
evaluator(value => {
result = typeof value === 'function' ? value(input) : value
}, { scope: {
evaluator(value => {
result = typeof value === 'function' ? value(input) : value
}, {
scope: {
// These are "magics" we'll make available to the x-mask:function:
'$input': input,
'$money': formatMoney.bind({ el }),
}})
},
autoEvaluateFunctions: false
})

return result
Expand Down
23 changes: 12 additions & 11 deletions packages/sort/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,21 +49,22 @@ function generateSortHandler(expression, evaluateLater) {

return (key, position) => {
// In the case of `x-sort="handleSort"`, let us call it manually...
Alpine.dontAutoEvaluateFunctions(() => {
handle(
// If a function is returned, call it with the key/position params...
received => {
if (typeof received === 'function') received(key, position)
},
// Provide $key and $position to the scope in case they want to call their own function...
{ scope: {
handle(
// If a function is returned, call it with the key/position params...
received => {
if (typeof received === 'function') received(key, position)
},
// Provide $key and $position to the scope in case they want to call their own function...
{
scope: {
// Supporting both `$item` AND `$key` ($key for BC)...
$key: key,
$item: key,
$position: position,
} },
)
})
},
autoEvaluateFunctions: false
},
)
}
}

Expand Down
Loading