diff --git a/packages/csp/src/evaluator.js b/packages/csp/src/evaluator.js
new file mode 100644
index 000000000..7a2d2ed30
--- /dev/null
+++ b/packages/csp/src/evaluator.js
@@ -0,0 +1,50 @@
+import { generateEvaluatorFromFunction, runIfTypeOfFunction } from 'alpinejs/src/evaluator'
+import { closestDataStack, mergeProxies } from 'alpinejs/src/scope'
+import { tryCatch } from 'alpinejs/src/utils/error'
+import { injectMagics } from 'alpinejs/src/magics'
+
+export function cspEvaluator(el, expression) {
+ let dataStack = generateDataStack(el)
+
+ // Return if the provided expression is already a function...
+ if (typeof expression === 'function') {
+ return generateEvaluatorFromFunction(dataStack, expression)
+ }
+
+ let evaluator = generateEvaluator(el, expression, dataStack)
+
+ return tryCatch.bind(null, el, expression, evaluator)
+}
+
+function generateDataStack(el) {
+ let overriddenMagics = {}
+
+ injectMagics(overriddenMagics, el)
+
+ return [overriddenMagics, ...closestDataStack(el)]
+}
+
+function generateEvaluator(el, expression, dataStack) {
+ return (receiver = () => {}, { scope = {}, params = [] } = {}) => {
+ let completeScope = mergeProxies([scope, ...dataStack])
+
+ if (completeScope[expression] === undefined) {
+ throwExpressionError(el, expression)
+ }
+
+ runIfTypeOfFunction(receiver, completeScope[expression], completeScope, params)
+ }
+}
+
+function throwExpressionError(el, expression) {
+ console.warn(
+`Alpine Error: Alpine is unable to interpret the following expression using the CSP-friendly build:
+
+"${expression}"
+
+Read more about the Alpine's CSP-friendly build restrictions here: https://alpinejs.dev/advanced/csp
+
+`,
+el
+ )
+}
diff --git a/packages/csp/src/index.js b/packages/csp/src/index.js
index 9b5b26c63..5638985c5 100644
--- a/packages/csp/src/index.js
+++ b/packages/csp/src/index.js
@@ -1,38 +1,37 @@
+/**
+ * Alpine CSP Build.
+ *
+ * Alpine allows you to use JavaScript directly inside your HTML. This is an
+ * incredibly powerful features. However, it violates the "unsafe-eval"
+ * Content Security Policy. This alternate Alpine build provides a
+ * more constrained API for Alpine that is also CSP-friendly...
+ */
import Alpine from 'alpinejs/src/alpine'
-Alpine.setEvaluator(cspCompliantEvaluator)
-
+/**
+ * _______________________________________________________
+ * The Evaluator
+ * -------------------------------------------------------
+ *
+ * By default, Alpine's evaluator "eval"-like utilties to
+ * interpret strings as runtime JS. We're going to use
+ * a more CSP-friendly evaluator for this instead.
+ */
+import { cspEvaluator } from './evaluator'
+
+Alpine.setEvaluator(cspEvaluator)
+
+/**
+ * The rest of this file bootstraps Alpine the way it is
+ * normally bootstrapped in the default build. We will
+ * set and define it's directives, magics, etc...
+ */
import { reactive, effect, stop, toRaw } from '@vue/reactivity'
+
Alpine.setReactivityEngine({ reactive, effect, release: stop, raw: toRaw })
import 'alpinejs/src/magics/index'
-import 'alpinejs/src/directives/index'
-
-import { closestDataStack, mergeProxies } from 'alpinejs/src/scope'
-import { injectMagics } from 'alpinejs/src/magics'
-import { generateEvaluatorFromFunction, runIfTypeOfFunction } from 'alpinejs/src/evaluator'
-import { tryCatch } from 'alpinejs/src/utils/error'
-
-function cspCompliantEvaluator(el, expression) {
- let overriddenMagics = {}
-
- injectMagics(overriddenMagics, el)
- let dataStack = [overriddenMagics, ...closestDataStack(el)]
-
- if (typeof expression === 'function') {
- return generateEvaluatorFromFunction(dataStack, expression)
- }
-
- let evaluator = (receiver = () => {}, { scope = {}, params = [] } = {}) => {
- let completeScope = mergeProxies([scope, ...dataStack])
-
- if (completeScope[expression] !== undefined) {
- runIfTypeOfFunction(receiver, completeScope[expression], completeScope, params)
- }
- }
-
- return tryCatch.bind(null, el, expression, evaluator)
-}
+import 'alpinejs/src/directives/index'
export default Alpine
diff --git a/packages/docs/src/en/advanced/csp.md b/packages/docs/src/en/advanced/csp.md
index f825645ba..c20c0e9ac 100644
--- a/packages/docs/src/en/advanced/csp.md
+++ b/packages/docs/src/en/advanced/csp.md
@@ -1,49 +1,87 @@
---
-order: 5
+order: 1
title: CSP
---
-# CSP (Content-Security Policy)
+# CSP (Content-Security Policy) Build
-In order for Alpine to be able to execute plain strings from HTML attributes as JavaScript expressions, for example `x-on:click="console.log()"`, it needs to rely on utilities that violate the "unsafe-eval" content security policy.
+In order for Alpine to be able to execute plain strings from HTML attributes as JavaScript expressions, for example `x-on:click="console.log()"`, it needs to rely on utilities that violate the "unsafe-eval" [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) that some applications may enforce for security purposes.
> Under the hood, Alpine doesn't actually use eval() itself because it's slow and problematic. Instead it uses Function declarations, which are much better, but still violate "unsafe-eval".
-In order to accommodate environments where this CSP is necessary, Alpine will offer an alternate build that doesn't violate "unsafe-eval", but has a more restrictive syntax.
+In order to accommodate environments where this CSP is necessary, Alpine offer's an alternate build that doesn't violate "unsafe-eval", but has a more restrictive syntax.
## Installation
-The CSP build hasn’t been officially released yet. In the meantime, you may build it from source. To do this, clone the [`alpinejs/alpine`](https://github.com/alpinejs/alpine) repository and run:
+You can use this build by either including it from a `
```
-This will generate a `/packages/csp/dist/` directory with the built files. After copying the appropriate file into your project, you can include it either via `
-