@randomdev/pulsedev 0.1.2
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/LICENSE +15 -0
- package/README.md +585 -0
- package/bin/cli.js +75 -0
- package/core/init.js +48 -0
- package/core/run.js +341 -0
- package/core/serverManager.js +299 -0
- package/helpers/counterChar.js +40 -0
- package/helpers/hashFingerprint.js +39 -0
- package/helpers/mimeTypes.js +75 -0
- package/helpers/resourceHasher.js +33 -0
- package/helpers/watcher.js +158 -0
- package/helpers/websocket.js +82 -0
- package/helpers/writter.js +85 -0
- package/package.json +37 -0
- package/web/assets/Arimo-VariableFont_wght.ttf +0 -0
- package/web/assets/favicon.png +0 -0
- package/web/assets/github.svg +1 -0
- package/web/assets/logo.png +0 -0
- package/web/assets/npm.svg +1 -0
- package/web/templates/documentation.html +65 -0
- package/web/templates/index.css +397 -0
- package/web/templates/index.html +64 -0
- package/web/templates/index.js +45 -0
- package/web/templates/pulsedev.json +10 -0
- package/web/templates/socket-client.js +6 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026, randomdev
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
10
|
+
WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
11
|
+
MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
12
|
+
ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
13
|
+
WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
14
|
+
ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
15
|
+
OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,585 @@
|
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img src="web/assets/logo.png" alt="PulseDev Logo" width="128">
|
|
3
|
+
</p>
|
|
4
|
+
|
|
5
|
+
# ⚡ PulseDev
|
|
6
|
+
|
|
7
|
+
**Native Node.js development server — static files, hot reload, and logging in 1.9MB**
|
|
8
|
+
|
|
9
|
+
[](https://www.npmjs.com/package/pulsedev)
|
|
10
|
+
[](https://nodejs.org/)
|
|
11
|
+
[](https://opensource.org/licenses/ISC)
|
|
12
|
+
[]()
|
|
13
|
+
[]()
|
|
14
|
+
|
|
15
|
+
---
|
|
16
|
+
|
|
17
|
+
# English
|
|
18
|
+
|
|
19
|
+
> Two commands. No `node_modules` in your project. No complicated setup.
|
|
20
|
+
> Just `pulsedev init` and `pulsedev run`.
|
|
21
|
+
|
|
22
|
+
## What is PulseDev?
|
|
23
|
+
|
|
24
|
+
PulseDev is a CLI tool for Node.js that starts a local HTTP server, watches your files for changes, and reloads the server automatically. It's built **100% with Node.js native modules**, with zero production dependencies.
|
|
25
|
+
|
|
26
|
+
Designed for developers who need a fast, lightweight dev environment without Webpack, Vite, or any other bundler.
|
|
27
|
+
|
|
28
|
+
**What it does:**
|
|
29
|
+
|
|
30
|
+
- 🖥️ native HTTP server (no Express, no Fastify)
|
|
31
|
+
- 👁️ file system watching with `fs.watch`, reloads on real changes
|
|
32
|
+
- 🔁 automatic cache busting: MD5 hash injection into `src` and `href` attributes
|
|
33
|
+
- 📝 HTTP request logging to terminal or persistent file with auto-rotation
|
|
34
|
+
- 📁 project scaffolding with a single command
|
|
35
|
+
- 🎨 extended MIME types: images, video, audio, fonts, documents, and more
|
|
36
|
+
- ⚙️ configurable via `pulsedev.json`
|
|
37
|
+
- ⚡ zero production dependencies — all native Node.js
|
|
38
|
+
|
|
39
|
+
## Installation
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
npm install -g pulsedev
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
Installation is **global**. No `node_modules` in your project.
|
|
46
|
+
|
|
47
|
+
> **Requirement:** Node.js 22 LTS or higher
|
|
48
|
+
|
|
49
|
+
**Local development:**
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
git clone https://github.com/pabloacisera/pulsedev_linux.git
|
|
53
|
+
cd pulsedev
|
|
54
|
+
npm link
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Quick start
|
|
58
|
+
|
|
59
|
+
### 1 — Initialize the project
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
pulsedev init
|
|
63
|
+
```
|
|
64
|
+
|
|
65
|
+
Automatically generates:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
your-project/
|
|
69
|
+
├── pulsedev.json ← configuration
|
|
70
|
+
└── src/
|
|
71
|
+
├── index.html ← main page
|
|
72
|
+
├── css/
|
|
73
|
+
│ └── index.css ← base styles
|
|
74
|
+
├── js/
|
|
75
|
+
│ ├── index.js ← main script
|
|
76
|
+
│ └── socket-client.js ← WebSocket client
|
|
77
|
+
└── assets/
|
|
78
|
+
└── Arimo-VariableFont_wght.ttf
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### 2 — Start the server
|
|
82
|
+
|
|
83
|
+
```bash
|
|
84
|
+
pulsedev run
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
The server runs at `http://localhost:3003` (or your configured port).
|
|
88
|
+
From that moment, any file change in `src/` triggers an automatic reload.
|
|
89
|
+
|
|
90
|
+
### 3 — Help and version
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
pulsedev --help
|
|
94
|
+
pulsedev --version
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Configuration — `pulsedev.json`
|
|
98
|
+
|
|
99
|
+
```json
|
|
100
|
+
{
|
|
101
|
+
"watchPath": ["*"],
|
|
102
|
+
"runFile": "index.html",
|
|
103
|
+
"port": 3003,
|
|
104
|
+
"outputPath": "terminal",
|
|
105
|
+
"ignoreExtensions": ["*.txt", "*.log", "*.env", "*.md"],
|
|
106
|
+
"debounceDelay": 0.5,
|
|
107
|
+
"recursive": true,
|
|
108
|
+
"logLimit": 5
|
|
109
|
+
}
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
| Property | Type | Description | Default |
|
|
113
|
+
|----------|------|-------------|---------|
|
|
114
|
+
| `watchPath` | `string[]` | Directories to watch. `["*"]` watches everything | `["*"]` |
|
|
115
|
+
| `runFile` | `string` | Entry HTML file | `"index.html"` |
|
|
116
|
+
| `port` | `number` | HTTP server port | `3003` |
|
|
117
|
+
| `outputPath` | `string` | Log destination: `"terminal"` or folder path | `"terminal"` |
|
|
118
|
+
| `ignoreExtensions` | `string[]` | Extensions the watcher ignores | `["*.txt","*.log","*.env","*.md"]` |
|
|
119
|
+
| `debounceDelay` | `number` | Seconds to wait before reload (prevents duplicate events) | `0.5` |
|
|
120
|
+
| `recursive` | `boolean` | Watch subdirectories. See note below. | `true` |
|
|
121
|
+
| `logLimit` | `number` | Max log file size in KB before rotation | `5` |
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
`recursive` controls whether the watcher enters subdirectories.
|
|
126
|
+
Its behavior depends on `watchPath`:
|
|
127
|
+
|
|
128
|
+
**Case 1 — `watchPath: ["*"]` with `recursive: false` → Invalid**
|
|
129
|
+
|
|
130
|
+
```json
|
|
131
|
+
{
|
|
132
|
+
"watchPath": ["*"],
|
|
133
|
+
"recursive": false
|
|
134
|
+
}
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
With `watchPath: ["*"]` the server listens from the project root.
|
|
138
|
+
If `recursive` were `false`, it would only detect changes in files directly
|
|
139
|
+
at the root (where normally only `pulsedev.json` lives). Changes in `src/`
|
|
140
|
+
would never be detected, breaking hot reload.
|
|
141
|
+
|
|
142
|
+
PulseDev detects this combination, ignores the `false`, forces `recursive: true`
|
|
143
|
+
automatically, and shows a warning explaining why.
|
|
144
|
+
|
|
145
|
+
**Case 2 — Specific `watchPath` with `recursive: false` → Valid**
|
|
146
|
+
|
|
147
|
+
```json
|
|
148
|
+
{
|
|
149
|
+
"watchPath": ["./src/css", "./src/js"],
|
|
150
|
+
"recursive": false
|
|
151
|
+
}
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
With specific paths the user controls exactly which directories to watch.
|
|
155
|
+
`recursive: false` is valid here: the watcher only monitors files at the root
|
|
156
|
+
of each configured path, without entering subdirectories.
|
|
157
|
+
PulseDev shows an info message confirming the active mode.
|
|
158
|
+
|
|
159
|
+
**Case 3 — Any `watchPath` with `recursive: true` or unset → Correct**
|
|
160
|
+
|
|
161
|
+
```json
|
|
162
|
+
{
|
|
163
|
+
"watchPath": ["*"],
|
|
164
|
+
"recursive": true
|
|
165
|
+
}
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Standard behavior. The watcher enters all subdirectories
|
|
169
|
+
of the configured paths. Recommended for most projects.
|
|
170
|
+
|
|
171
|
+
## How hot reload works
|
|
172
|
+
|
|
173
|
+
PulseDev does not reload on every file system event. Before triggering a
|
|
174
|
+
reload, it computes the MD5 hash of the modified file and compares it to the
|
|
175
|
+
previous hash stored in memory. **It only reloads if the content actually changed.**
|
|
176
|
+
|
|
177
|
+
```
|
|
178
|
+
file saved to disk
|
|
179
|
+
→ fs.watch detects the event
|
|
180
|
+
→ stat() verifies it's a file (not a directory)
|
|
181
|
+
→ compareChanges() computes MD5 and compares with in-memory Map
|
|
182
|
+
→ if changed → debounce → reloadServer()
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
Additionally, when serving HTML, the server automatically injects the hash of
|
|
186
|
+
each referenced CSS and JS file:
|
|
187
|
+
|
|
188
|
+
```html
|
|
189
|
+
<!-- what you write -->
|
|
190
|
+
<link rel="stylesheet" href="css/index.css">
|
|
191
|
+
|
|
192
|
+
<!-- what the browser receives -->
|
|
193
|
+
<link rel="stylesheet" href="css/index.css?v=a3f2c1b4">
|
|
194
|
+
```
|
|
195
|
+
|
|
196
|
+
### WebSocket auto-reload system
|
|
197
|
+
|
|
198
|
+
PulseDev notifies the browser when the server restarts via a persistent **WebSocket** connection. The flow:
|
|
199
|
+
|
|
200
|
+
```
|
|
201
|
+
file changed → watcher detects → reloadServer()
|
|
202
|
+
→ closeAllConnections() closes WS sockets
|
|
203
|
+
→ HTTP server restarts with new config
|
|
204
|
+
→ browser detects closed socket (onclose)
|
|
205
|
+
→ location.reload() refreshes the page
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
**Injected WebSocket client** (`socket-client.js`):
|
|
209
|
+
|
|
210
|
+
```javascript
|
|
211
|
+
let ws = new WebSocket("ws://localhost:" + location.port);
|
|
212
|
+
ws.onclose = () => location.reload(); // server restarted
|
|
213
|
+
ws.onerror = () => setTimeout(() => location.reload(), 500); // connection error
|
|
214
|
+
```
|
|
215
|
+
|
|
216
|
+
**Why WebSocket over SSE or polling?**
|
|
217
|
+
|
|
218
|
+
- **Bidirectional**: server can notify the client instantly
|
|
219
|
+
- **Low overhead**: single open TCP connection, no multiple HTTP requests
|
|
220
|
+
- **Zero latency**: no polling interval, notification is immediate
|
|
221
|
+
- **Native in the browser**: no additional dependencies
|
|
222
|
+
- **Persistent**: connection stays open while the browser is on the page
|
|
223
|
+
|
|
224
|
+
## Logging system
|
|
225
|
+
|
|
226
|
+
By default, HTTP requests are logged to `logs/requests.log`. A native Worker Thread
|
|
227
|
+
handles file rotation when it exceeds the `logLimit` threshold, without blocking
|
|
228
|
+
the main thread:
|
|
229
|
+
|
|
230
|
+
```
|
|
231
|
+
server running at http://localhost:3003
|
|
232
|
+
- watching for changes in /path/to/project
|
|
233
|
+
- log written to logs/requests.log
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
To see logs in the terminal instead, set `"outputPath"` to `"terminal"` in your
|
|
237
|
+
`pulsedev.json`.
|
|
238
|
+
|
|
239
|
+
## Supported content types
|
|
240
|
+
|
|
241
|
+
The server recognizes and correctly serves over 40 file types:
|
|
242
|
+
|
|
243
|
+
| Category | Extensions |
|
|
244
|
+
|----------|------------|
|
|
245
|
+
| Web | `html`, `css`, `js`, `mjs`, `json`, `xml`, `txt` |
|
|
246
|
+
| Images | `png`, `jpg`, `gif`, `webp`, `avif`, `svg`, `ico`, `bmp`, `tiff` |
|
|
247
|
+
| Fonts | `ttf`, `otf`, `woff`, `woff2`, `eot` |
|
|
248
|
+
| Audio | `mp3`, `wav`, `ogg`, `aac`, `flac`, `opus` |
|
|
249
|
+
| Video | `mp4`, `webm`, `avi`, `mov`, `mkv` |
|
|
250
|
+
| Documents | `pdf`, `doc`, `docx`, `xls`, `xlsx`, `zip` |
|
|
251
|
+
|
|
252
|
+
## Project architecture
|
|
253
|
+
|
|
254
|
+
```
|
|
255
|
+
pulsedev/
|
|
256
|
+
├── bin/
|
|
257
|
+
│ └── cli.js ← CLI entry point (init / run / --help / --version)
|
|
258
|
+
├── core/
|
|
259
|
+
│ ├── init.js ← Project scaffolding
|
|
260
|
+
│ ├── run.js ← Re-export of serverManager
|
|
261
|
+
│ └── serverManager.js ← HTTP server, state, and reload
|
|
262
|
+
├── helpers/
|
|
263
|
+
│ ├── watcher.js ← File watcher with fs.watch and debounce
|
|
264
|
+
│ ├── hashFingerprint.js ← MD5 comparison and cache busting
|
|
265
|
+
│ ├── resourceHasher.js ← Hash injection into HTML
|
|
266
|
+
│ ├── mimeTypes.js ← MIME types and binary extensions
|
|
267
|
+
│ ├── writter.js ← File read/write + log Worker
|
|
268
|
+
│ └── counterChar.js ← Worker Thread for log rotation
|
|
269
|
+
└── web/
|
|
270
|
+
├── assets/
|
|
271
|
+
│ ├── Arimo-VariableFont_wght.ttf
|
|
272
|
+
│ ├── github.svg
|
|
273
|
+
│ └── npm.svg
|
|
274
|
+
└── templates/
|
|
275
|
+
├── pulsedev.json ← Default config
|
|
276
|
+
├── index.html ← Welcome page
|
|
277
|
+
├── index.css ← Base styles
|
|
278
|
+
├── index.js ← Main script
|
|
279
|
+
└── socket-client.js ← WebSocket client
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Built with
|
|
283
|
+
|
|
284
|
+
PulseDev is built **100% with Node.js native modules**. Zero production dependencies.
|
|
285
|
+
|
|
286
|
+
| Module | Usage |
|
|
287
|
+
|--------|-------|
|
|
288
|
+
| `node:http` | HTTP server |
|
|
289
|
+
| `node:fs` / `node:fs/promises` | File reading, writing, and watching |
|
|
290
|
+
| `node:path` | Path resolution |
|
|
291
|
+
| `node:os` | Hostname for logs |
|
|
292
|
+
| `node:crypto` | MD5 hash for fingerprinting and cache busting |
|
|
293
|
+
| `node:worker_threads` | Worker Thread for log rotation |
|
|
294
|
+
| `node:util` → `styleText` | Terminal colors |
|
|
295
|
+
| `node:url` | `__dirname` resolution in ESM |
|
|
296
|
+
|
|
297
|
+
---
|
|
298
|
+
|
|
299
|
+
---
|
|
300
|
+
|
|
301
|
+
# Español
|
|
302
|
+
|
|
303
|
+
> Dos comandos. Sin `node_modules` en tu proyecto. Sin configuración complicada.
|
|
304
|
+
> Solo `pulsedev init` y `pulsedev run`.
|
|
305
|
+
|
|
306
|
+
## ¿Qué es PulseDev?
|
|
307
|
+
|
|
308
|
+
PulseDev es una herramienta CLI para Node.js que levanta un servidor HTTP local, vigila los cambios en tus archivos y recarga el servidor automáticamente. Está construida **100% con módulos nativos de Node.js**, sin ninguna dependencia de producción.
|
|
309
|
+
|
|
310
|
+
Está pensada para developers que necesitan un entorno de desarrollo rápido y liviano, sin instalar Webpack, Vite ni nada por el estilo.
|
|
311
|
+
|
|
312
|
+
**¿Qué hace exactamente?**
|
|
313
|
+
|
|
314
|
+
- 🖥️ Levanta un servidor HTTP nativo (sin Express, sin Fastify)
|
|
315
|
+
- 👁️ Vigila el sistema de archivos con `fs.watch` y recarga ante cambios reales
|
|
316
|
+
- 🔁 Cache busting automático: inyecta hashes MD5 en los `src` y `href` del HTML
|
|
317
|
+
- 📝 Logs de peticiones HTTP en terminal o archivo persistente con rotación automática
|
|
318
|
+
- 📁 Genera la estructura base del proyecto con un solo comando
|
|
319
|
+
- 🎨 Tipos MIME extendidos: imágenes, video, audio, fuentes, documentos y más
|
|
320
|
+
- ⚙️ Configurable vía `pulsedev.json`
|
|
321
|
+
- ⚡ Cero dependencias de producción — todo Node.js nativo
|
|
322
|
+
|
|
323
|
+
## Instalación
|
|
324
|
+
|
|
325
|
+
```bash
|
|
326
|
+
npm install -g pulsedev
|
|
327
|
+
```
|
|
328
|
+
|
|
329
|
+
La instalación es **global**. No se genera `node_modules` en tu proyecto.
|
|
330
|
+
|
|
331
|
+
> **Requisito:** Node.js 22 LTS o superior
|
|
332
|
+
|
|
333
|
+
**Desarrollo local:**
|
|
334
|
+
|
|
335
|
+
```bash
|
|
336
|
+
git clone https://github.com/pabloacisera/pulsedev_linux.git
|
|
337
|
+
cd pulsedev
|
|
338
|
+
npm link
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
## Uso rápido
|
|
342
|
+
|
|
343
|
+
### 1 — Inicializar el proyecto
|
|
344
|
+
|
|
345
|
+
```bash
|
|
346
|
+
pulsedev init
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
Genera automáticamente:
|
|
350
|
+
|
|
351
|
+
```
|
|
352
|
+
tu-proyecto/
|
|
353
|
+
├── pulsedev.json ← configuración
|
|
354
|
+
└── src/
|
|
355
|
+
├── index.html ← página principal
|
|
356
|
+
├── css/
|
|
357
|
+
│ └── index.css ← estilos base
|
|
358
|
+
├── js/
|
|
359
|
+
│ ├── index.js ← script principal
|
|
360
|
+
│ └── socket-client.js ← cliente WebSocket
|
|
361
|
+
└── assets/
|
|
362
|
+
└── Arimo-VariableFont_wght.ttf
|
|
363
|
+
```
|
|
364
|
+
|
|
365
|
+
### 2 — Levantar el servidor
|
|
366
|
+
|
|
367
|
+
```bash
|
|
368
|
+
pulsedev run
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
El servidor queda corriendo en `http://localhost:3003` (o el puerto configurado).
|
|
372
|
+
Desde ese momento, cualquier cambio en los archivos de `src/` dispara un reload automático.
|
|
373
|
+
|
|
374
|
+
### 3 — Ayuda y versión
|
|
375
|
+
|
|
376
|
+
```bash
|
|
377
|
+
pulsedev --help
|
|
378
|
+
pulsedev --version
|
|
379
|
+
```
|
|
380
|
+
|
|
381
|
+
## Configuración — `pulsedev.json`
|
|
382
|
+
|
|
383
|
+
```json
|
|
384
|
+
{
|
|
385
|
+
"watchPath": ["*"],
|
|
386
|
+
"runFile": "index.html",
|
|
387
|
+
"port": 3003,
|
|
388
|
+
"outputPath": "terminal",
|
|
389
|
+
"ignoreExtensions": ["*.txt", "*.log", "*.env", "*.md"],
|
|
390
|
+
"debounceDelay": 0.5,
|
|
391
|
+
"recursive": true,
|
|
392
|
+
"logLimit": 5
|
|
393
|
+
}
|
|
394
|
+
```
|
|
395
|
+
|
|
396
|
+
| Propiedad | Tipo | Descripción | Default |
|
|
397
|
+
|-----------|------|-------------|---------|
|
|
398
|
+
| `watchPath` | `string[]` | Directorios a vigilar. `["*"]` vigila todo el proyecto | `["*"]` |
|
|
399
|
+
| `runFile` | `string` | Archivo HTML de entrada | `"index.html"` |
|
|
400
|
+
| `port` | `number` | Puerto del servidor HTTP | `3003` |
|
|
401
|
+
| `outputPath` | `string` | Destino de logs: `"terminal"` o ruta de carpeta | `"terminal"` |
|
|
402
|
+
| `ignoreExtensions` | `string[]` | Extensiones que el watcher ignora | `["*.txt","*.log","*.env","*.md"]` |
|
|
403
|
+
| `debounceDelay` | `number` | Segundos de espera antes del reload (evita eventos repetidos) | `0.5` |
|
|
404
|
+
| `recursive` | `boolean` | Vigilar subdirectorios. Ver nota importante abajo. | `true` |
|
|
405
|
+
| `logLimit` | `number` | Tamaño máximo del log en KB antes de rotar | `5` |
|
|
406
|
+
|
|
407
|
+
---
|
|
408
|
+
|
|
409
|
+
`recursive` controla si el watcher entra en subdirectorios al vigilar cambios.
|
|
410
|
+
Su comportamiento depende de cómo esté configurado `watchPath`:
|
|
411
|
+
|
|
412
|
+
**Caso 1 — `watchPath: ["*"]` con `recursive: false` → Configuración inválida**
|
|
413
|
+
|
|
414
|
+
```json
|
|
415
|
+
{
|
|
416
|
+
"watchPath": ["*"],
|
|
417
|
+
"recursive": false
|
|
418
|
+
}
|
|
419
|
+
```
|
|
420
|
+
|
|
421
|
+
Con `watchPath: ["*"]` el servidor escucha desde la raíz del proyecto.
|
|
422
|
+
Si `recursive` fuera `false`, solo detectaría cambios en archivos ubicados
|
|
423
|
+
directamente en esa raíz (donde normalmente solo está `pulsedev.json`).
|
|
424
|
+
Ningún cambio en `src/` sería detectado y el hot reload nunca funcionaría.
|
|
425
|
+
|
|
426
|
+
PulseDev detecta esta combinación, ignora el `false`, fuerza `recursive: true`
|
|
427
|
+
automáticamente y muestra una advertencia en terminal explicando el motivo.
|
|
428
|
+
|
|
429
|
+
**Caso 2 — `watchPath` con rutas propias y `recursive: false` → Válido**
|
|
430
|
+
|
|
431
|
+
```json
|
|
432
|
+
{
|
|
433
|
+
"watchPath": ["./src/css", "./src/js"],
|
|
434
|
+
"recursive": false
|
|
435
|
+
}
|
|
436
|
+
```
|
|
437
|
+
|
|
438
|
+
Con rutas específicas el usuario controla exactamente qué directorios vigilar.
|
|
439
|
+
`recursive: false` es válido acá: el watcher escucha solo los archivos
|
|
440
|
+
en la raíz de cada ruta configurada, sin entrar en subdirectorios.
|
|
441
|
+
PulseDev muestra un mensaje informativo confirmando el modo activo.
|
|
442
|
+
|
|
443
|
+
**Caso 3 — Cualquier `watchPath` con `recursive: true` o sin configurar → Correcto**
|
|
444
|
+
|
|
445
|
+
```json
|
|
446
|
+
{
|
|
447
|
+
"watchPath": ["*"],
|
|
448
|
+
"recursive": true
|
|
449
|
+
}
|
|
450
|
+
```
|
|
451
|
+
|
|
452
|
+
Comportamiento estándar. El watcher entra en todos los subdirectorios
|
|
453
|
+
de las rutas configuradas. Es el modo recomendado para la mayoría de los proyectos.
|
|
454
|
+
|
|
455
|
+
## Cómo funciona el hot reload
|
|
456
|
+
|
|
457
|
+
PulseDev no recarga ante cualquier evento del sistema de archivos. Antes de disparar el
|
|
458
|
+
reload, calcula el hash MD5 del archivo modificado y lo compara con el hash anterior
|
|
459
|
+
guardado en memoria. **Solo recarga si el contenido realmente cambió.**
|
|
460
|
+
|
|
461
|
+
```
|
|
462
|
+
archivo guardado en disco
|
|
463
|
+
→ fs.watch detecta el evento
|
|
464
|
+
→ stat() verifica que sea un archivo (no carpeta)
|
|
465
|
+
→ compareChanges() calcula MD5 y compara con Map en memoria
|
|
466
|
+
→ si cambió → debounce → reloadServer()
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
Adicionalmente, al servir el HTML el servidor inyecta automáticamente el hash del
|
|
470
|
+
contenido actual de cada CSS y JS referenciado:
|
|
471
|
+
|
|
472
|
+
```html
|
|
473
|
+
<!-- lo que escribís -->
|
|
474
|
+
<link rel="stylesheet" href="css/index.css">
|
|
475
|
+
|
|
476
|
+
<!-- lo que el browser recibe -->
|
|
477
|
+
<link rel="stylesheet" href="css/index.css?v=a3f2c1b4">
|
|
478
|
+
```
|
|
479
|
+
|
|
480
|
+
### Sistema de reload automático por WebSocket
|
|
481
|
+
|
|
482
|
+
PulseDev notifica al navegador cuando el servidor se reinicia mediante una conexión **WebSocket** persistente. El flujo es:
|
|
483
|
+
|
|
484
|
+
```
|
|
485
|
+
archivo modificado → watcher detecta cambio → reloadServer()
|
|
486
|
+
→ closeAllConnections() cierra sockets WS
|
|
487
|
+
→ servidor HTTP se reinicia con nueva config
|
|
488
|
+
→ navegador detecta socket cerrado (onclose)
|
|
489
|
+
→ location.reload() actualiza la página
|
|
490
|
+
```
|
|
491
|
+
|
|
492
|
+
**Cliente WebSocket inyectado** (`socket-client.js`):
|
|
493
|
+
|
|
494
|
+
```javascript
|
|
495
|
+
let ws = new WebSocket("ws://localhost:" + location.port);
|
|
496
|
+
ws.onclose = () => location.reload(); // servidor reiniciado
|
|
497
|
+
ws.onerror = () => setTimeout(() => location.reload(), 500); // error de conexión
|
|
498
|
+
```
|
|
499
|
+
|
|
500
|
+
**¿Por qué WebSocket en vez de SSE o polling?**
|
|
501
|
+
|
|
502
|
+
- **Bidireccional**: el servidor puede notificar al cliente al instante
|
|
503
|
+
- **Bajo overhead**: una sola conexión TCP abierta, no múltiples requests HTTP
|
|
504
|
+
- **Latencia cero**: no hay polling interval, la notificación es inmediata
|
|
505
|
+
- **Nativo en el browser**: no requiere dependencias adicionales
|
|
506
|
+
- **Persistente**: la conexión permanece abierta mientras el navegador esté en la página
|
|
507
|
+
|
|
508
|
+
## Sistema de logs
|
|
509
|
+
|
|
510
|
+
Por defecto, las peticiones HTTP se registran en `logs/requests.log`. Un Worker Thread
|
|
511
|
+
nativo se encarga de rotar el archivo cuando supera el límite configurado en `logLimit`,
|
|
512
|
+
sin bloquear el hilo principal:
|
|
513
|
+
|
|
514
|
+
```
|
|
515
|
+
servidor corriendo en http://localhost:3003
|
|
516
|
+
- watching for changes in /path/to/project
|
|
517
|
+
- log written to logs/requests.log
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
Si preferís ver los logs en terminal, cambiá `"outputPath"` a `"terminal"` en tu
|
|
521
|
+
`pulsedev.json`.
|
|
522
|
+
|
|
523
|
+
## Tipos de contenido soportados
|
|
524
|
+
|
|
525
|
+
El servidor reconoce y sirve correctamente más de 40 tipos de archivos:
|
|
526
|
+
|
|
527
|
+
| Categoría | Extensiones |
|
|
528
|
+
|-----------|-------------|
|
|
529
|
+
| Web | `html`, `css`, `js`, `mjs`, `json`, `xml`, `txt` |
|
|
530
|
+
| Imágenes | `png`, `jpg`, `gif`, `webp`, `avif`, `svg`, `ico`, `bmp`, `tiff` |
|
|
531
|
+
| Fuentes | `ttf`, `otf`, `woff`, `woff2`, `eot` |
|
|
532
|
+
| Audio | `mp3`, `wav`, `ogg`, `aac`, `flac`, `opus` |
|
|
533
|
+
| Video | `mp4`, `webm`, `avi`, `mov`, `mkv` |
|
|
534
|
+
| Documentos | `pdf`, `doc`, `docx`, `xls`, `xlsx`, `zip` |
|
|
535
|
+
|
|
536
|
+
## Arquitectura del proyecto
|
|
537
|
+
|
|
538
|
+
```
|
|
539
|
+
pulsedev/
|
|
540
|
+
├── bin/
|
|
541
|
+
│ └── cli.js ← Punto de entrada CLI (init / run / --help / --version)
|
|
542
|
+
├── core/
|
|
543
|
+
│ ├── init.js ← Genera la estructura del proyecto
|
|
544
|
+
│ ├── run.js ← Re-export de serverManager
|
|
545
|
+
│ └── serverManager.js ← Servidor HTTP, estado y reload
|
|
546
|
+
├── helpers/
|
|
547
|
+
│ ├── watcher.js ← File watcher con fs.watch y debounce
|
|
548
|
+
│ ├── hashFingerprint.js ← Comparación MD5 y cache busting
|
|
549
|
+
│ ├── resourceHasher.js ← Inyección de hashes en HTML
|
|
550
|
+
│ ├── mimeTypes.js ← Tipos MIME y extensiones binarias
|
|
551
|
+
│ ├── writter.js ← Lectura/escritura de archivos + Worker de logs
|
|
552
|
+
│ └── counterChar.js ← Worker Thread para rotación de logs
|
|
553
|
+
└── web/
|
|
554
|
+
├── assets/
|
|
555
|
+
│ ├── Arimo-VariableFont_wght.ttf
|
|
556
|
+
│ ├── github.svg
|
|
557
|
+
│ └── npm.svg
|
|
558
|
+
└── templates/
|
|
559
|
+
├── pulsedev.json ← Config por defecto
|
|
560
|
+
├── index.html ← Página de bienvenida
|
|
561
|
+
├── index.css ← Estilos base
|
|
562
|
+
├── index.js ← Script principal
|
|
563
|
+
└── socket-client.js ← Cliente WebSocket
|
|
564
|
+
```
|
|
565
|
+
|
|
566
|
+
## Tecnologías utilizadas
|
|
567
|
+
|
|
568
|
+
PulseDev está construido **100% con módulos nativos de Node.js**. Sin dependencias de producción.
|
|
569
|
+
|
|
570
|
+
| Módulo | Uso |
|
|
571
|
+
|--------|-----|
|
|
572
|
+
| `node:http` | Servidor HTTP |
|
|
573
|
+
| `node:fs` / `node:fs/promises` | Lectura, escritura y vigilancia de archivos |
|
|
574
|
+
| `node:path` | Resolución de rutas |
|
|
575
|
+
| `node:os` | Hostname para logs |
|
|
576
|
+
| `node:crypto` | Hash MD5 para fingerprinting y cache busting |
|
|
577
|
+
| `node:worker_threads` | Worker Thread para rotación de logs |
|
|
578
|
+
| `node:util` → `styleText` | Colores en terminal |
|
|
579
|
+
| `node:url` | Resolución de `__dirname` en ESM |
|
|
580
|
+
|
|
581
|
+
---
|
|
582
|
+
|
|
583
|
+
## Autor
|
|
584
|
+
|
|
585
|
+
Desarrollado por **randomdev** · Licencia [ISC](https://opensource.org/licenses/ISC)
|
package/bin/cli.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { styleText } from "node:util";
|
|
3
|
+
import { argv, cwd } from "node:process";
|
|
4
|
+
import fs from "node:fs";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
// --- Guard de versión: PulseDev requiere Node.js 22+ por fs.watch recursive en Linux ---
|
|
9
|
+
const major = parseInt(process.versions.node.split(".")[0], 10);
|
|
10
|
+
if (Number.isNaN(major) || major < 22) {
|
|
11
|
+
console.error(
|
|
12
|
+
styleText("bgRed", " error ") +
|
|
13
|
+
` PulseDev requiere Node.js 22 o superior. Versión detectada: v${process.versions.node}`
|
|
14
|
+
);
|
|
15
|
+
process.exit(1);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
const currentDirectory = cwd();
|
|
19
|
+
|
|
20
|
+
// --- Leer la versión desde el package.json de forma segura en ESM ---
|
|
21
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
22
|
+
const __dirname = path.dirname(__filename);
|
|
23
|
+
const packageJsonPath = path.resolve(__dirname, "../package.json");
|
|
24
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, "utf8"));
|
|
25
|
+
const version = packageJson.version;
|
|
26
|
+
|
|
27
|
+
// verificar que comando se ejecuto
|
|
28
|
+
let executeCommand = argv[2];
|
|
29
|
+
let helpCommands = `
|
|
30
|
+
${styleText("bold", "PulseDev - Vigilante de archivos para Node.js")}
|
|
31
|
+
|
|
32
|
+
${styleText("underline", "Uso:")}
|
|
33
|
+
pulsedev <comando>
|
|
34
|
+
|
|
35
|
+
${styleText("underline", "Comandos disponibles:")}
|
|
36
|
+
init Genera el archivo de configuración "pulsedev.json"
|
|
37
|
+
run Ejecuta el servidor y vigila los cambios
|
|
38
|
+
|
|
39
|
+
${styleText("underline", "Opciones globales:")}
|
|
40
|
+
--version, -v Muestra la versión
|
|
41
|
+
--help, -h Muestra la ayuda
|
|
42
|
+
|
|
43
|
+
${styleText("underline", "Opciones de run:")}
|
|
44
|
+
--list-flags Muestra los flags disponibles (= claves de tu pulsedev.json)
|
|
45
|
+
--persist Si se usan flags, persiste el override al JSON
|
|
46
|
+
(por defecto los flags son override en memoria, no destructivo)
|
|
47
|
+
--<clave> <valor> Cualquier clave de tu pulsedev.json (ej: --port 4000)
|
|
48
|
+
|
|
49
|
+
${styleText("underline", "Ejemplos:")}
|
|
50
|
+
pulsedev init Crea la configuración inicial
|
|
51
|
+
pulsedev run Inicia el vigilante
|
|
52
|
+
pulsedev run --list-flags Muestra los flags disponibles
|
|
53
|
+
pulsedev run --port 4000 Override en memoria (no toca el JSON)
|
|
54
|
+
pulsedev run --port 4000 --persist Override + escribe el JSON
|
|
55
|
+
|
|
56
|
+
${styleText("dim", "Documentación: https://github.com/pabloacisera/pulsedev_linux")}
|
|
57
|
+
`;
|
|
58
|
+
|
|
59
|
+
// Estructura de control de comandos
|
|
60
|
+
if (executeCommand === "init") {
|
|
61
|
+
import("../core/init.js").then(m => m.runInit(currentDirectory)).catch(e => { console.error(e); process.exit(1); });
|
|
62
|
+
} else if (executeCommand === "run") {
|
|
63
|
+
import("../core/run.js").then(m => m.runCommand(currentDirectory)).catch(e => { console.error(e); process.exit(1); });
|
|
64
|
+
} else if (executeCommand === "--help" || executeCommand === "-h") {
|
|
65
|
+
console.log(helpCommands);
|
|
66
|
+
} else if (executeCommand === "--version" || executeCommand === "-v") {
|
|
67
|
+
// Mostramos la versión dinámica con un toque de estilo
|
|
68
|
+
console.log(`v${version}`);
|
|
69
|
+
} else {
|
|
70
|
+
console.log(
|
|
71
|
+
styleText("bgMagenta", " Advertencia ") +
|
|
72
|
+
" No se ha ejecutado ningún comando válido. Usá " +
|
|
73
|
+
styleText("bold", "--help") + " para ver las opciones."
|
|
74
|
+
);
|
|
75
|
+
}
|