@alpinejs/docs 3.14.7-revision.1 → 3.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alpinejs/docs",
3
- "version": "3.14.7-revision.1",
3
+ "version": "3.15.0",
4
4
  "description": "The documentation for Alpine",
5
5
  "author": "Caleb Porzio",
6
6
  "license": "MIT"
@@ -5,11 +5,11 @@ title: CSP
5
5
 
6
6
  # CSP (Content-Security Policy) Build
7
7
 
8
- 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.
8
+ In order for Alpine to execute JavaScript expressions from HTML attributes like `x-on:click="console.log()"`, it needs to use utilities that violate the "unsafe-eval" [Content Security Policy](https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP) that some applications enforce for security purposes.
9
9
 
10
10
  > 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".
11
11
 
12
- 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.
12
+ Alpine offers an alternate build that doesn't violate "unsafe-eval" and supports most of Alpine's inline expression syntax.
13
13
 
14
14
  <a name="installation"></a>
15
15
  ## Installation
@@ -46,98 +46,139 @@ Alpine.start()
46
46
  <a name="basic-example"></a>
47
47
  ## Basic Example
48
48
 
49
- To provide a glimpse of how using the CSP build might feel, here is a copy-pastable HTML file with a working counter component using a common CSP setup:
49
+ Here's a working counter component using Alpine's CSP build. Notice how most expressions work exactly like regular Alpine:
50
50
 
51
51
  ```alpine
52
52
  <html>
53
53
  <head>
54
54
  <meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'nonce-a23gbfz9e'">
55
-
56
55
  <script defer nonce="a23gbfz9e" src="https://cdn.jsdelivr.net/npm/@alpinejs/csp@3.x.x/dist/cdn.min.js"></script>
57
56
  </head>
58
-
59
57
  <body>
60
- <div x-data="counter">
61
- <button x-on:click="increment"></button>
58
+ <div x-data="{ count: 0, message: 'Hello' }">
59
+ <button x-on:click="count++">Increment</button>
60
+ <button x-on:click="count = 0">Reset</button>
62
61
 
63
62
  <span x-text="count"></span>
63
+ <span x-text="message + ' World'"></span>
64
+ <span x-show="count > 5">Count is greater than 5!</span>
64
65
  </div>
65
-
66
- <script nonce="a23gbfz9e">
67
- document.addEventListener('alpine:init', () => {
68
- Alpine.data('counter', () => {
69
- return {
70
- count: 1,
71
-
72
- increment() {
73
- this.count++;
74
- },
75
- }
76
- })
77
- })
78
- </script>
79
66
  </body>
80
67
  </html>
81
68
  ```
82
69
 
83
- <a name="api-restrictions"></a>
84
- ## API Restrictions
85
-
86
- Since Alpine can no longer interpret strings as plain JavaScript, it has to parse and construct JavaScript functions from them manually.
70
+ <a name="whats-supported"></a>
71
+ ## What's Supported
87
72
 
88
- Due to this limitation, you must use `Alpine.data` to register your `x-data` objects, and must reference properties and methods from it by key only.
89
-
90
- For example, an inline component like this will not work.
73
+ The CSP build supports most JavaScript expressions you'd want to use in Alpine:
91
74
 
75
+ ### Object and Array Literals
92
76
  ```alpine
93
- <!-- Bad -->
94
- <div x-data="{ count: 1 }">
95
- <button @click="count++">Increment</button>
77
+ <!-- These work -->
78
+ <div x-data="{ user: { name: 'John', age: 30 }, items: [1, 2, 3] }">
79
+ <span x-text="user.name"></span>
80
+ <span x-text="items[0]"></span>
81
+ </div>
82
+ ```
96
83
 
97
- <span x-text="count"></span>
84
+ ### Basic Operations
85
+ ```alpine
86
+ <!-- ✅ These work -->
87
+ <div x-data="{ count: 5, name: 'Alpine' }">
88
+ <span x-text="count + 10"></span>
89
+ <span x-text="count > 3"></span>
90
+ <span x-text="count === 5 ? 'Yes' : 'No'"></span>
91
+ <span x-text="'Hello ' + name"></span>
92
+ <div x-show="!loading && count > 0"></div>
98
93
  </div>
99
94
  ```
100
95
 
101
- However, breaking out the expressions into external APIs, the following is valid with the CSP build:
96
+ ### Assignments and Updates
97
+ ```alpine
98
+ <!-- ✅ These work -->
99
+ <div x-data="{ count: 0, user: { name: '' } }">
100
+ <button x-on:click="count++">Increment</button>
101
+ <button x-on:click="count = 0">Reset</button>
102
+ <input x-model="user.name">
103
+ </div>
104
+ ```
102
105
 
106
+ ### Method Calls
103
107
  ```alpine
104
- <!-- Good -->
105
- <div x-data="counter">
106
- <button @click="increment">Increment</button>
108
+ <!-- These work -->
109
+ <div x-data="{ items: ['a', 'b'], getMessage: () => 'Hello' }">
110
+ <span x-text="getMessage()"></span>
111
+ <button x-on:click="items.push('c')">Add Item</button>
112
+ </div>
113
+ ```
107
114
 
108
- <span x-text="count"></span>
115
+ ### Global Variables and Functions
116
+ ```alpine
117
+ <!-- ✅ These work -->
118
+ <div x-data="{ count: 42 }">
119
+ <button x-on:click="console.log('Count is:', count)">Log Count</button>
120
+ <span x-text="Math.max(count, 100)"></span>
121
+ <span x-text="parseInt('123') + count"></span>
122
+ <span x-text="JSON.stringify({ value: count })"></span>
109
123
  </div>
110
124
  ```
111
125
 
112
- ```js
113
- Alpine.data('counter', () => ({
114
- count: 1,
126
+ <a name="whats-not-supported"></a>
127
+ ## What's Not Supported
128
+
129
+ Some advanced JavaScript features aren't supported:
115
130
 
116
- increment() {
117
- this.count++
118
- },
119
- }))
131
+ ```alpine
132
+ <!-- ❌ These don't work -->
133
+ <div x-data>
134
+ <!-- Arrow functions -->
135
+ <button x-on:click="() => console.log('hi')">Bad</button>
136
+
137
+ <!-- Destructuring -->
138
+ <div x-text="{ name } = user">Bad</div>
139
+
140
+ <!-- Template literals -->
141
+ <div x-text="`Hello ${name}`">Bad</div>
142
+
143
+ <!-- Spread operator -->
144
+ <div x-data="{ ...defaults }">Bad</div>
145
+ </div>
120
146
  ```
121
147
 
122
- The CSP build supports accessing nested properties (property accessors) using the dot notation.
148
+ <a name="when-to-extract-logic"></a>
149
+ ## When to Extract Logic
150
+
151
+ While the CSP build supports simple inline expressions, you'll want to extract complex logic into dedicated functions or Alpine.data() components for better organization:
123
152
 
124
153
  ```alpine
125
- <!-- This works too -->
126
- <div x-data="counter">
127
- <button @click="foo.increment">Increment</button>
154
+ <!-- Instead of this -->
155
+ <div x-data="{ users: [] }" x-show="users.filter(u => u.active && u.role === 'admin').length > 0">
156
+ ```
128
157
 
129
- <span x-text="foo.count"></span>
130
- </div>
158
+ ```alpine
159
+ <!-- Do this -->
160
+ <div x-data="userManager" x-show="hasActiveAdmins">
161
+
162
+ <script nonce="...">
163
+ Alpine.data('userManager', () => ({
164
+ users: [],
165
+
166
+ get hasActiveAdmins() {
167
+ return this.users.filter(u => u.active && u.role === 'admin').length > 0
168
+ }
169
+ }))
170
+ </script>
131
171
  ```
132
172
 
133
- ```js
134
- Alpine.data('counter', () => ({
135
- foo: {
136
- count: 1,
137
-
138
- increment() {
139
- this.count++
140
- },
141
- },
142
- }))
173
+ This approach makes your code more readable, testable, and maintainable, especially for complex applications.
174
+
175
+ <a name="csp-headers"></a>
176
+ ## CSP Headers
177
+
178
+ Here's an example CSP header that works with Alpine's CSP build:
179
+
180
+ ```
181
+ Content-Security-Policy: default-src 'self'; script-src 'nonce-[random]' 'strict-dynamic';
143
182
  ```
183
+
184
+ The key is removing `'unsafe-eval'` from your `script-src` directive while still allowing your nonce-based scripts to run.
@@ -33,7 +33,7 @@ This is by far the simplest way to get started with Alpine. Include the followin
33
33
  Notice the `@3.x.x` in the provided CDN link. This will pull the latest version of Alpine version 3. For stability in production, it's recommended that you hardcode the latest version in the CDN link.
34
34
 
35
35
  ```alpine
36
- <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.14.7/dist/cdn.min.js"></script>
36
+ <script defer src="https://cdn.jsdelivr.net/npm/alpinejs@3.15.0/dist/cdn.min.js"></script>
37
37
  ```
38
38
 
39
39
  That's it! Alpine is now available for use inside your page.
@@ -228,7 +228,7 @@ For example:
228
228
 
229
229
  ```alpine
230
230
  <div x-data="{ open: false }">
231
- <button>Open Dialog</button>
231
+ <button @click="open = true">Open Dialog</button>
232
232
 
233
233
  <div x-show="open" x-trap.noscroll="open">
234
234
  Dialog Contents
@@ -250,3 +250,15 @@ By adding keys to each node, we can accomplish this like so:
250
250
  Now that there are "keys" on the `<li>`s, Morph will match them in both trees and move them accordingly.
251
251
 
252
252
  You can configure what Morph considers a "key" with the `key:` configuration option. [More on that here](#lifecycle-hooks)
253
+
254
+ <a name="alpine-morph-between"></a>
255
+ ## Alpine.morphBetween()
256
+
257
+ The `Alpine.morphBetween(startMarker, endMarker, newHtml, options)` method allows you to morph a range of DOM nodes between two marker elements based on passed in HTML. This is useful when you want to update only a specific section of the DOM without providing a single root node.
258
+
259
+ | Parameter | Description |
260
+ | --- | --- |
261
+ | `startMarker` | A DOM node (typically a comment node) that marks the beginning of the range to morph |
262
+ | `endMarker` | A DOM node (typically a comment node) that marks the end of the range to morph |
263
+ | `newHtml` | A string of HTML or a DOM element to replace the content between the markers |
264
+ | `options` | An object of options (same as `Alpine.morph()`) |