node-turbo 1.0.1 → 1.1.1
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/.c8rc.json +3 -1
- package/README.md +120 -21
- package/docs/API.md +250 -30
- package/lib/express/express-turbo-stream.js +5 -0
- package/lib/koa/koa-turbo-stream.js +3 -1
- package/lib/turbo-element.js +8 -2
- package/lib/turbo-frame.js +5 -0
- package/lib/turbo-readable.js +9 -2
- package/lib/turbo-stream-element.js +27 -21
- package/lib/turbo-stream.js +52 -96
- package/lib/ws/ws-turbo-stream.js +8 -0
- package/package.json +30 -15
- package/test/unit/core/turbo-element.test.js +29 -0
- package/test/unit/core/turbo-readable.test.js +0 -3
- package/test/unit/core/turbo-stream-element.test.js +7 -0
- package/test/unit/core/turbo-stream.test.js +31 -0
package/.c8rc.json
CHANGED
package/README.md
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
# node-turbo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
node-turbo is a library for Node.js to assist with the server side of [37signals](https://37signals.com)' [Hotwire Turbo](https://turbo.hotwired.dev) framework. It provides classes and functions for Web servers and also convenience functions for the frameworks [Koa](https://koajs.com) and [Express](https://expressjs.com) as well as for [WebSocket](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket) and [SSE](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events).
|
|
4
4
|
|
|
5
5
|
This documentation assumes that you are familiar with Turbo and its [handbook](https://turbo.hotwired.dev/handbook/introduction).
|
|
6
6
|
|
|
7
7
|
## Table of Contents
|
|
8
|
+
|
|
8
9
|
- [Installation](#installation)
|
|
9
10
|
- [Compatibility](#compatibility)
|
|
10
11
|
- [Browser](#browser)
|
|
@@ -14,10 +15,14 @@ This documentation assumes that you are familiar with Turbo and its [handbook](h
|
|
|
14
15
|
- [Usage](#usage)
|
|
15
16
|
- [Basics\/Standalone](#basicsstandalone)
|
|
16
17
|
- [Turbo Stream](#turbo-stream)
|
|
18
|
+
- [Action shortcut functions](#action-shortcut-functions)
|
|
19
|
+
- [Additional attributes](#additional-attributes)
|
|
17
20
|
- [Target multiple elements](#target-multiple-elements)
|
|
21
|
+
- [Refresh action](#refresh-action)
|
|
18
22
|
- [Custom actions](#custom-actions)
|
|
19
23
|
- [Using the Node.js streams API](#using-the-nodejs-streams-api)
|
|
20
|
-
- [Turbo Frame](#turbo-frame)
|
|
24
|
+
- [Turbo Frame](#turbo-frame)
|
|
25
|
+
- [Additional attributes](#additional-attributes-1)
|
|
21
26
|
- [Request helper functions](#request-helper-functions)
|
|
22
27
|
- [isTurboStreamRequest(request)](#isturbostreamrequestrequest)
|
|
23
28
|
- [isTurboFrameRequest(request)](#isturboframerequestrequest)
|
|
@@ -32,7 +37,8 @@ This documentation assumes that you are familiar with Turbo and its [handbook](h
|
|
|
32
37
|
- [License](#license)
|
|
33
38
|
|
|
34
39
|
## Installation
|
|
35
|
-
|
|
40
|
+
|
|
41
|
+
```shell
|
|
36
42
|
npm install node-turbo
|
|
37
43
|
```
|
|
38
44
|
|
|
@@ -49,11 +55,11 @@ node-turbo has been tested with:
|
|
|
49
55
|
|
|
50
56
|
| Name | Version(s) |
|
|
51
57
|
| :--- | :--- |
|
|
52
|
-
| [Node.js](https://nodejs.org/) | 16.6
|
|
53
|
-
| [Hotwire Turbo](https://turbo.hotwired.dev/) | 7.3.0
|
|
54
|
-
| [Koa](https://koajs.com/) | 2.14.2 |
|
|
55
|
-
| [Express](https://expressjs.com/) | 4.18.2 |
|
|
56
|
-
| [ws](https://github.com/websockets/ws) | 8.15.1 |
|
|
58
|
+
| [Node.js](https://nodejs.org/) | 16.6 - 21.5.0 |
|
|
59
|
+
| [Hotwire Turbo](https://turbo.hotwired.dev/) | 7.3.0 - 8.0.4 |
|
|
60
|
+
| [Koa](https://koajs.com/) | 2.14.2 - 2.15.3 |
|
|
61
|
+
| [Express](https://expressjs.com/) | 4.18.2 - 4.19.2 |
|
|
62
|
+
| [ws](https://github.com/websockets/ws) | 8.15.1 - 8.18.0 |
|
|
57
63
|
|
|
58
64
|
## API docs
|
|
59
65
|
See [`/docs/API.md`](./docs/API.md) for a documentation of all node-turbo classes and functions.
|
|
@@ -85,7 +91,8 @@ This will render the following HTML fragment:
|
|
|
85
91
|
</turbo-stream>
|
|
86
92
|
```
|
|
87
93
|
|
|
88
|
-
|
|
94
|
+
##### Action shortcut functions
|
|
95
|
+
For all supported [official actions](https://turbo.hotwired.dev/handbook/streams#stream-messages-and-actions) (`append`, `prepend`, `replace`, `update`, `remove`, `before`, `after`, `morph` and `refresh`), there are chainable shortcut functions:
|
|
89
96
|
|
|
90
97
|
```javascript
|
|
91
98
|
import { TurboStream } from 'node-turbo';
|
|
@@ -115,14 +122,41 @@ Result:
|
|
|
115
122
|
</turbo-stream>
|
|
116
123
|
```
|
|
117
124
|
|
|
125
|
+
##### Additional attributes
|
|
126
|
+
If you want/need to add additional attributes to an Turbo Stream element, you can always pass an object instead of the target ID string. Attributes with value `null` will be rendered as boolean attributes.
|
|
127
|
+
|
|
128
|
+
```javascript
|
|
129
|
+
import { TurboStream } from 'node-turbo';
|
|
130
|
+
|
|
131
|
+
const ts = new TurboStream()
|
|
132
|
+
.morph({
|
|
133
|
+
target: 'target-id',
|
|
134
|
+
'children-only': null,
|
|
135
|
+
custom: 'attribute'
|
|
136
|
+
},
|
|
137
|
+
'<p>My content</p>');
|
|
138
|
+
|
|
139
|
+
const html = ts.render();
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
Result:
|
|
143
|
+
|
|
144
|
+
```html
|
|
145
|
+
<turbo-stream action="morph" target="target-id" children-only custom="attribute">
|
|
146
|
+
<template>
|
|
147
|
+
<p>My content</p>
|
|
148
|
+
</template>
|
|
149
|
+
</turbo-stream>
|
|
150
|
+
```
|
|
151
|
+
|
|
118
152
|
##### Target multiple elements
|
|
119
|
-
If you want to [target multiple elements](https://turbo.hotwired.dev/handbook/streams#actions-with-multiple-targets), you can use the `[action]All()` function:
|
|
153
|
+
If you want to [target multiple elements](https://turbo.hotwired.dev/handbook/streams#actions-with-multiple-targets), you can use the `[action]All()` function (not available when using the `refresh` action):
|
|
120
154
|
|
|
121
155
|
```javascript
|
|
122
156
|
import { TurboStream } from 'node-turbo';
|
|
123
157
|
|
|
124
|
-
|
|
125
|
-
|
|
158
|
+
const ts = new TurboStream().appendAll('.my-targets', '<p>My content</p>');
|
|
159
|
+
const html = ts.render();
|
|
126
160
|
```
|
|
127
161
|
|
|
128
162
|
Result:
|
|
@@ -135,13 +169,45 @@ Result:
|
|
|
135
169
|
</turbo-stream>
|
|
136
170
|
```
|
|
137
171
|
|
|
172
|
+
##### Refresh action
|
|
173
|
+
The action `refresh` works differently as it does not have a target or targets.
|
|
174
|
+
|
|
175
|
+
```javascript
|
|
176
|
+
import { TurboStream } from 'node-turbo';
|
|
177
|
+
|
|
178
|
+
let ts = new TurboStream().refresh();
|
|
179
|
+
let html = ts.render();
|
|
180
|
+
// Renders <turbo-stream action="refresh"></turbo-stream>
|
|
181
|
+
|
|
182
|
+
ts.refresh('1234');
|
|
183
|
+
html = ts.render();
|
|
184
|
+
// Renders <turbo-stream action="refresh" request-id="1234"></turbo-stream>
|
|
185
|
+
|
|
186
|
+
ts.refresh({
|
|
187
|
+
'request-id': '1234',
|
|
188
|
+
'custom': 'param'
|
|
189
|
+
});
|
|
190
|
+
html = ts.render();
|
|
191
|
+
// Renders <turbo-stream action="refresh" request-id="1234" custom="param"></turbo-stream>
|
|
192
|
+
```
|
|
193
|
+
|
|
138
194
|
##### Custom actions
|
|
139
195
|
If you want to use [custom actions](https://turbo.hotwired.dev/handbook/streams#custom-actions), you can use the `custom()`/`customAll()` functions:
|
|
140
196
|
```javascript
|
|
141
197
|
import { TurboStream } from 'node-turbo';
|
|
142
198
|
|
|
143
|
-
|
|
144
|
-
|
|
199
|
+
const ts = new TurboStream().custom('custom-action', 'target-id', '<p>My content</p>');
|
|
200
|
+
const html = ts.render();
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
Result:
|
|
204
|
+
|
|
205
|
+
```html
|
|
206
|
+
<turbo-stream action="custom-action" target="target-id">
|
|
207
|
+
<template>
|
|
208
|
+
<p>My content</p>
|
|
209
|
+
</template>
|
|
210
|
+
</turbo-stream>
|
|
145
211
|
```
|
|
146
212
|
|
|
147
213
|
##### Using the Node.js streams API
|
|
@@ -154,17 +220,25 @@ import { TurboStream } from 'node-turbo';
|
|
|
154
220
|
const ts = new TurboStream();
|
|
155
221
|
const readable = ts.createReadableStream();
|
|
156
222
|
|
|
157
|
-
readable.pipe(process.stdout)
|
|
223
|
+
readable.pipe(process.stdout);
|
|
224
|
+
|
|
225
|
+
// These elements get piped immediately:
|
|
226
|
+
ts
|
|
227
|
+
.append('target-id', '<p>My content</p>')
|
|
228
|
+
.replace('target-id-2', '<p>New content</p>')
|
|
229
|
+
.remove('target-id-3');
|
|
158
230
|
```
|
|
159
231
|
|
|
160
232
|
See [Koa](#koa), [SSE](#sse) or [WebSocket](#websocket) for further examples.
|
|
161
233
|
|
|
234
|
+
***
|
|
235
|
+
|
|
162
236
|
#### Turbo Frame
|
|
163
237
|
```javascript
|
|
164
238
|
import { TurboFrame } from 'node-turbo';
|
|
165
239
|
|
|
166
240
|
const tf = new TurboFrame('my-id', '<p>content</p>');
|
|
167
|
-
const html =
|
|
241
|
+
const html = tf.render();
|
|
168
242
|
```
|
|
169
243
|
|
|
170
244
|
This will render the following HTML fragment:
|
|
@@ -173,12 +247,37 @@ This will render the following HTML fragment:
|
|
|
173
247
|
<turbo-frame id="my-id">
|
|
174
248
|
<p>My content</p>
|
|
175
249
|
</turbo-stream>
|
|
176
|
-
```
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
##### Additional attributes
|
|
253
|
+
You can add additional attributes by passing an object instead of the `id` string:
|
|
254
|
+
|
|
255
|
+
```javascript
|
|
256
|
+
import { TurboFrame } from 'node-turbo';
|
|
257
|
+
|
|
258
|
+
const tf = new TurboFrame({
|
|
259
|
+
id: 'my-id',
|
|
260
|
+
custom: 'foo'
|
|
261
|
+
}, '<p>content</p>');
|
|
262
|
+
const html = tf.render();
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
Result:
|
|
266
|
+
|
|
267
|
+
```html
|
|
268
|
+
<turbo-frame id="my-id" custom="foo">
|
|
269
|
+
<p>My content</p>
|
|
270
|
+
</turbo-stream>
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
***
|
|
177
274
|
|
|
178
275
|
#### Request Helper Functions
|
|
276
|
+
|
|
179
277
|
node-turbo also provides the following helper functions. You can use these to adapt the behaviour of your server to the differend kind of requests.
|
|
180
278
|
|
|
181
279
|
##### isTurboStreamRequest(request)
|
|
280
|
+
|
|
182
281
|
```javascript
|
|
183
282
|
import { isTurboStreamRequest } from 'node-turbo';
|
|
184
283
|
|
|
@@ -426,7 +525,7 @@ const httpServer = http.createServer((req, res) => {
|
|
|
426
525
|
padding: 10px;
|
|
427
526
|
}
|
|
428
527
|
</style>
|
|
429
|
-
<script type="module" src="https://unpkg.com/@hotwired/turbo@8.0.
|
|
528
|
+
<script type="module" src="https://unpkg.com/@hotwired/turbo@8.0.4/dist/turbo.es2017-esm.js"></script>
|
|
430
529
|
<script>
|
|
431
530
|
var eventSource = new EventSource('/sse');
|
|
432
531
|
eventSource.onmessage = function(event) {
|
|
@@ -513,7 +612,7 @@ app.use(async (ctx, next) => {
|
|
|
513
612
|
padding: 10px;
|
|
514
613
|
}
|
|
515
614
|
</style>
|
|
516
|
-
<script type="module" src="https://unpkg.com/@hotwired/turbo@8.0.
|
|
615
|
+
<script type="module" src="https://unpkg.com/@hotwired/turbo@8.0.4/dist/turbo.es2017-esm.js"></script>
|
|
517
616
|
<script>
|
|
518
617
|
var eventSource = new EventSource('/sse');
|
|
519
618
|
eventSource.onmessage = function(event) {
|
|
@@ -590,7 +689,7 @@ app.get('/', async (req, res) => {
|
|
|
590
689
|
padding: 10px;
|
|
591
690
|
}
|
|
592
691
|
</style>
|
|
593
|
-
<script type="module" src="https://unpkg.com/@hotwired/turbo@8.0.
|
|
692
|
+
<script type="module" src="https://unpkg.com/@hotwired/turbo@8.0.4/dist/turbo.es2017-esm.js"></script>
|
|
594
693
|
<script>
|
|
595
694
|
var eventSource = new EventSource('/sse');
|
|
596
695
|
eventSource.onmessage = function(event) {
|
|
@@ -617,4 +716,4 @@ app.listen(config.port);
|
|
|
617
716
|
```
|
|
618
717
|
## License
|
|
619
718
|
|
|
620
|
-
node-turbo is © 2024 Walter Krivanek
|
|
719
|
+
node-turbo is © 2024 Walter Krivanek and released under the [MIT license](https://mit-license.org).
|