serve-reload 1.0.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/LICENSE +21 -0
- package/README.md +127 -0
- package/dist/cli.d.ts +21 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +111 -0
- package/dist/cli.js.map +1 -0
- package/dist/glob.d.ts +13 -0
- package/dist/glob.d.ts.map +1 -0
- package/dist/glob.js +90 -0
- package/dist/glob.js.map +1 -0
- package/dist/index.d.ts +12 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +15 -0
- package/dist/index.js.map +1 -0
- package/dist/injector.d.ts +11 -0
- package/dist/injector.d.ts.map +1 -0
- package/dist/injector.js +26 -0
- package/dist/injector.js.map +1 -0
- package/dist/mime.d.ts +6 -0
- package/dist/mime.d.ts.map +1 -0
- package/dist/mime.js +55 -0
- package/dist/mime.js.map +1 -0
- package/dist/server.d.ts +56 -0
- package/dist/server.d.ts.map +1 -0
- package/dist/server.js +286 -0
- package/dist/server.js.map +1 -0
- package/dist/watcher.d.ts +32 -0
- package/dist/watcher.d.ts.map +1 -0
- package/dist/watcher.js +90 -0
- package/dist/watcher.js.map +1 -0
- package/package.json +36 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 serve-reload contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
# serve-reload
|
|
2
|
+
|
|
3
|
+
Zero-dependency static file server with live reload for development.
|
|
4
|
+
|
|
5
|
+
Most static dev servers with live reload are either abandoned — accumulating security vulnerabilities flagged by `npm audit` — or bloated with features and dependencies far beyond what a simple dev server needs. serve-reload takes a different approach: it just serves and reloads, with zero dependencies, eliminating the problem of transitive security issues at the root.
|
|
6
|
+
|
|
7
|
+
## Features
|
|
8
|
+
|
|
9
|
+
- **Zero dependencies** — only Node.js built-ins
|
|
10
|
+
- **Live reload** — via Server-Sent Events (no WebSocket library needed)
|
|
11
|
+
- **CSS hot inject** — CSS changes apply instantly without a full page reload
|
|
12
|
+
- **SPA mode** — fallback routing for single-page apps
|
|
13
|
+
- **Proxy** — forward API requests to another server
|
|
14
|
+
- **CORS enabled** — by default
|
|
15
|
+
- **Glob ignore patterns** — `*.map`, `src/temp`, `**/test`, etc.
|
|
16
|
+
- **Fast startup** — nothing to install beyond Node.js
|
|
17
|
+
- **CLI + API** — use from the terminal or programmatically
|
|
18
|
+
|
|
19
|
+
## Install
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install -g serve-reload
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
Or use directly with npx:
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
npx serve-reload ./public
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
## CLI Usage
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
serve-reload [root] [options]
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
| Option | Description | Default |
|
|
38
|
+
|---|---|---|
|
|
39
|
+
| `-p, --port <n>` | Port number (auto-fallback if busy) | `3000` |
|
|
40
|
+
| `--host <addr>` | Bind address | `0.0.0.0` |
|
|
41
|
+
| `-o, --open` | Open browser on start | off |
|
|
42
|
+
| `--no-reload` | Disable live reload | enabled |
|
|
43
|
+
| `--no-cors` | Disable CORS headers | enabled |
|
|
44
|
+
| `--spa <file>` | SPA fallback file | — |
|
|
45
|
+
| `--quiet` | Suppress request logs | off |
|
|
46
|
+
| `--ignore <patterns>` | Comma-separated ignore patterns | — |
|
|
47
|
+
| `--proxy <route:url>` | Proxy route to URL (repeatable) | — |
|
|
48
|
+
|
|
49
|
+
### Examples
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
# Serve current directory on port 3000
|
|
53
|
+
serve-reload
|
|
54
|
+
|
|
55
|
+
# Serve ./dist on port 8080, open browser
|
|
56
|
+
serve-reload ./dist -p 8080 --open
|
|
57
|
+
|
|
58
|
+
# SPA mode (all unknown routes → index.html)
|
|
59
|
+
serve-reload ./build --spa index.html
|
|
60
|
+
|
|
61
|
+
# Ignore source maps and temp files
|
|
62
|
+
serve-reload --ignore "*.map,*.min.*,temp"
|
|
63
|
+
|
|
64
|
+
# Proxy API requests
|
|
65
|
+
serve-reload --proxy /api:https://api.example.com
|
|
66
|
+
|
|
67
|
+
# Multiple proxies
|
|
68
|
+
serve-reload --proxy /api:https://api.example.com --proxy /auth:http://localhost:4000
|
|
69
|
+
|
|
70
|
+
# Disable live reload (just a static server)
|
|
71
|
+
serve-reload ./public --no-reload
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## Ignore Patterns
|
|
75
|
+
|
|
76
|
+
The `--ignore` option controls which file changes **do not trigger a reload**. Ignored files are still served normally over HTTP — they just won't cause the browser to refresh.
|
|
77
|
+
|
|
78
|
+
Supported patterns:
|
|
79
|
+
|
|
80
|
+
| Pattern | Example | Matches |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `node_modules` | Plain name | Any path segment named `node_modules` |
|
|
83
|
+
| `*.map` | Extension glob | `dist/bundle.js.map` |
|
|
84
|
+
| `*.min.*` | Multi-extension | `bundle.min.js` |
|
|
85
|
+
| `src/temp` | Path prefix | `src/temp` and `src/temp/file.js` |
|
|
86
|
+
| `**/test` | Recursive glob | `test`, `src/test`, `deep/nested/test` |
|
|
87
|
+
| `?.js` | Single character | `a.js` but not `ab.js` |
|
|
88
|
+
|
|
89
|
+
Default ignored: `node_modules`, `.git`, `.DS_Store`
|
|
90
|
+
|
|
91
|
+
## Programmatic API
|
|
92
|
+
|
|
93
|
+
```ts
|
|
94
|
+
import { ServeReload } from "serve-reload";
|
|
95
|
+
|
|
96
|
+
const server = new ServeReload({
|
|
97
|
+
root: "./public",
|
|
98
|
+
port: 8080,
|
|
99
|
+
open: true,
|
|
100
|
+
spa: "index.html",
|
|
101
|
+
ignore: ["*.map", "temp"],
|
|
102
|
+
proxy: {
|
|
103
|
+
"/api": "https://api.example.com",
|
|
104
|
+
},
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
server.start();
|
|
108
|
+
|
|
109
|
+
// Later...
|
|
110
|
+
await server.stop();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## How It Works
|
|
114
|
+
|
|
115
|
+
1. **Static serving** — Node's built-in `http` module serves files with correct MIME types
|
|
116
|
+
2. **HTML injection** — A tiny `<script>` is injected into HTML responses that opens an SSE connection
|
|
117
|
+
3. **File watching** — `fs.watch` (recursive) monitors the served directory for changes
|
|
118
|
+
4. **Reload signal** — When a non-CSS file changes, all connected browsers reload. When a CSS file changes, stylesheets are hot-swapped without a full reload
|
|
119
|
+
5. **Reconnect** — If the server restarts, the browser automatically reconnects and reloads
|
|
120
|
+
|
|
121
|
+
## Requirements
|
|
122
|
+
|
|
123
|
+
Node.js ≥ 18
|
|
124
|
+
|
|
125
|
+
## License
|
|
126
|
+
|
|
127
|
+
MIT
|
package/dist/cli.d.ts
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* serve-reload CLI
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* serve-reload [root] [options]
|
|
7
|
+
*
|
|
8
|
+
* Options:
|
|
9
|
+
* -p, --port <n> Port number (default: 3000)
|
|
10
|
+
* --host <addr> Bind address (default: 0.0.0.0)
|
|
11
|
+
* -o, --open Open browser on start
|
|
12
|
+
* --no-reload Disable live reload
|
|
13
|
+
* --no-cors Disable CORS headers
|
|
14
|
+
* --spa <file> SPA fallback file (e.g. index.html)
|
|
15
|
+
* --quiet Suppress request logging
|
|
16
|
+
* --ignore <dirs> Comma-separated dirs to ignore
|
|
17
|
+
* --proxy <r:url> Proxy route to URL (repeatable)
|
|
18
|
+
* --help Show this help
|
|
19
|
+
*/
|
|
20
|
+
export {};
|
|
21
|
+
//# sourceMappingURL=cli.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA;;;;;;;;;;;;;;;;;GAiBG"}
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
/**
|
|
4
|
+
* serve-reload CLI
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* serve-reload [root] [options]
|
|
8
|
+
*
|
|
9
|
+
* Options:
|
|
10
|
+
* -p, --port <n> Port number (default: 3000)
|
|
11
|
+
* --host <addr> Bind address (default: 0.0.0.0)
|
|
12
|
+
* -o, --open Open browser on start
|
|
13
|
+
* --no-reload Disable live reload
|
|
14
|
+
* --no-cors Disable CORS headers
|
|
15
|
+
* --spa <file> SPA fallback file (e.g. index.html)
|
|
16
|
+
* --quiet Suppress request logging
|
|
17
|
+
* --ignore <dirs> Comma-separated dirs to ignore
|
|
18
|
+
* --proxy <r:url> Proxy route to URL (repeatable)
|
|
19
|
+
* --help Show this help
|
|
20
|
+
*/
|
|
21
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
+
const server_js_1 = require("./server.js");
|
|
23
|
+
const args = process.argv.slice(2);
|
|
24
|
+
if (args.includes("--help")) {
|
|
25
|
+
console.log(`
|
|
26
|
+
serve-reload — Zero-dependency static file server with live reload
|
|
27
|
+
|
|
28
|
+
Usage:
|
|
29
|
+
serve-reload [root] [options]
|
|
30
|
+
|
|
31
|
+
Options:
|
|
32
|
+
-p, --port <n> Port number (default: 3000)
|
|
33
|
+
--host <addr> Bind address (default: 0.0.0.0)
|
|
34
|
+
-o, --open Open browser on start
|
|
35
|
+
--no-reload Disable live reload
|
|
36
|
+
--no-cors Disable CORS headers
|
|
37
|
+
--spa <file> SPA fallback file (e.g. index.html)
|
|
38
|
+
--quiet Suppress request logging
|
|
39
|
+
--ignore <dirs> Comma-separated directory names to ignore
|
|
40
|
+
--proxy <r:url> Proxy route to URL (repeatable, e.g. /api:https://api.example.com)
|
|
41
|
+
--help Show this help
|
|
42
|
+
|
|
43
|
+
Examples:
|
|
44
|
+
serve-reload
|
|
45
|
+
serve-reload ./dist -p 8080 --open
|
|
46
|
+
serve-reload ./public --spa index.html
|
|
47
|
+
serve-reload --proxy /api:https://api.example.com --proxy /auth:http://localhost:4000
|
|
48
|
+
`);
|
|
49
|
+
process.exit(0);
|
|
50
|
+
}
|
|
51
|
+
function getArg(flags) {
|
|
52
|
+
for (const flag of flags) {
|
|
53
|
+
const idx = args.indexOf(flag);
|
|
54
|
+
if (idx !== -1 && idx + 1 < args.length) {
|
|
55
|
+
return args[idx + 1];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
function hasFlag(flags) {
|
|
61
|
+
return flags.some((f) => args.includes(f));
|
|
62
|
+
}
|
|
63
|
+
// The first non-flag argument is the root directory
|
|
64
|
+
let root = ".";
|
|
65
|
+
for (const arg of args) {
|
|
66
|
+
if (!arg.startsWith("-")) {
|
|
67
|
+
root = arg;
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const server = new server_js_1.ServeReload({
|
|
72
|
+
root,
|
|
73
|
+
port: parseInt(getArg(["-p", "--port"]) ?? "3000", 10),
|
|
74
|
+
host: getArg(["--host"]) ?? "0.0.0.0",
|
|
75
|
+
open: hasFlag(["-o", "--open"]),
|
|
76
|
+
reload: !hasFlag(["--no-reload"]),
|
|
77
|
+
cors: !hasFlag(["--no-cors"]),
|
|
78
|
+
quiet: hasFlag(["--quiet"]),
|
|
79
|
+
spa: getArg(["--spa"]) ?? null,
|
|
80
|
+
ignore: getArg(["--ignore"])?.split(",").map((s) => s.trim()) ?? [],
|
|
81
|
+
proxy: parseProxy(),
|
|
82
|
+
});
|
|
83
|
+
function parseProxy() {
|
|
84
|
+
const result = {};
|
|
85
|
+
for (let i = 0; i < args.length; i++) {
|
|
86
|
+
if (args[i] === "--proxy" && i + 1 < args.length) {
|
|
87
|
+
const val = args[i + 1];
|
|
88
|
+
const sep = val.indexOf(":", val.startsWith("/") ? 1 : 0);
|
|
89
|
+
if (sep === -1) {
|
|
90
|
+
console.error(` Invalid proxy format: ${val} (expected /route:url)`);
|
|
91
|
+
process.exit(1);
|
|
92
|
+
}
|
|
93
|
+
const route = val.slice(0, sep);
|
|
94
|
+
const target = val.slice(sep + 1);
|
|
95
|
+
result[route] = target;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
server.start();
|
|
101
|
+
// Graceful shutdown
|
|
102
|
+
process.on("SIGINT", async () => {
|
|
103
|
+
console.log("\n Shutting down...");
|
|
104
|
+
await server.stop();
|
|
105
|
+
process.exit(0);
|
|
106
|
+
});
|
|
107
|
+
process.on("SIGTERM", async () => {
|
|
108
|
+
await server.stop();
|
|
109
|
+
process.exit(0);
|
|
110
|
+
});
|
|
111
|
+
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|
package/dist/glob.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Minimal glob-to-regex converter. Zero dependencies.
|
|
3
|
+
*
|
|
4
|
+
* Supported patterns:
|
|
5
|
+
* "node_modules" → matches any path segment named node_modules
|
|
6
|
+
* "*.map" → matches any file ending in .map
|
|
7
|
+
* "src/temp" → matches exact relative prefix
|
|
8
|
+
* "**/test" → matches "test" at any depth
|
|
9
|
+
* "*.min.*" → matches e.g. bundle.min.js
|
|
10
|
+
* ".git" → matches .git directory/file
|
|
11
|
+
*/
|
|
12
|
+
export declare function createMatcher(patterns: string[]): (filePath: string) => boolean;
|
|
13
|
+
//# sourceMappingURL=glob.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.d.ts","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,QAAQ,EAAE,MAAM,KAAK,OAAO,CAU/E"}
|
package/dist/glob.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal glob-to-regex converter. Zero dependencies.
|
|
4
|
+
*
|
|
5
|
+
* Supported patterns:
|
|
6
|
+
* "node_modules" → matches any path segment named node_modules
|
|
7
|
+
* "*.map" → matches any file ending in .map
|
|
8
|
+
* "src/temp" → matches exact relative prefix
|
|
9
|
+
* "**/test" → matches "test" at any depth
|
|
10
|
+
* "*.min.*" → matches e.g. bundle.min.js
|
|
11
|
+
* ".git" → matches .git directory/file
|
|
12
|
+
*/
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.createMatcher = createMatcher;
|
|
15
|
+
function createMatcher(patterns) {
|
|
16
|
+
if (patterns.length === 0)
|
|
17
|
+
return () => false;
|
|
18
|
+
const matchers = patterns.map((p) => compileSingle(p));
|
|
19
|
+
return (filePath) => {
|
|
20
|
+
// Normalize to forward slashes for matching
|
|
21
|
+
const normalized = filePath.replace(/\\/g, "/");
|
|
22
|
+
return matchers.some((m) => m(normalized));
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
function compileSingle(pattern) {
|
|
26
|
+
const p = pattern.replace(/\\/g, "/");
|
|
27
|
+
// Plain name without glob chars or slashes → match any segment
|
|
28
|
+
if (!p.includes("*") && !p.includes("/") && !p.includes("?")) {
|
|
29
|
+
return (filePath) => {
|
|
30
|
+
const segments = filePath.split("/");
|
|
31
|
+
return segments.includes(p);
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
// Patterns with slashes but no globs → match as prefix (dir) or exact
|
|
35
|
+
if (!p.includes("*") && !p.includes("?") && p.includes("/")) {
|
|
36
|
+
return (filePath) => filePath === p || filePath.startsWith(p + "/");
|
|
37
|
+
}
|
|
38
|
+
// Convert glob to regex
|
|
39
|
+
const regexStr = globToRegex(p);
|
|
40
|
+
const regex = new RegExp(regexStr);
|
|
41
|
+
return (filePath) => regex.test(filePath);
|
|
42
|
+
}
|
|
43
|
+
function globToRegex(glob) {
|
|
44
|
+
let result = "";
|
|
45
|
+
let i = 0;
|
|
46
|
+
// If pattern doesn't start with ** or a path separator, allow matching
|
|
47
|
+
// against any segment boundary (e.g. "*.map" matches "dir/foo.map")
|
|
48
|
+
const matchAnywhere = !glob.startsWith("/") && !glob.startsWith("**/");
|
|
49
|
+
if (matchAnywhere) {
|
|
50
|
+
result += "(?:^|/)";
|
|
51
|
+
}
|
|
52
|
+
else {
|
|
53
|
+
result += "^";
|
|
54
|
+
}
|
|
55
|
+
while (i < glob.length) {
|
|
56
|
+
const ch = glob[i];
|
|
57
|
+
if (ch === "*" && glob[i + 1] === "*") {
|
|
58
|
+
// ** matches any depth (including zero segments)
|
|
59
|
+
if (glob[i + 2] === "/") {
|
|
60
|
+
result += "(?:.+/)?";
|
|
61
|
+
i += 3;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
result += ".*";
|
|
65
|
+
i += 2;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
else if (ch === "*") {
|
|
69
|
+
// * matches anything except /
|
|
70
|
+
result += "[^/]*";
|
|
71
|
+
i++;
|
|
72
|
+
}
|
|
73
|
+
else if (ch === "?") {
|
|
74
|
+
// ? matches single char except /
|
|
75
|
+
result += "[^/]";
|
|
76
|
+
i++;
|
|
77
|
+
}
|
|
78
|
+
else {
|
|
79
|
+
// Escape regex special chars
|
|
80
|
+
result += escapeRegex(ch);
|
|
81
|
+
i++;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
result += "$";
|
|
85
|
+
return result;
|
|
86
|
+
}
|
|
87
|
+
function escapeRegex(ch) {
|
|
88
|
+
return ch.replace(/[.+^${}()|[\]\\]/g, "\\$&");
|
|
89
|
+
}
|
|
90
|
+
//# sourceMappingURL=glob.js.map
|
package/dist/glob.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"glob.js","sourceRoot":"","sources":["../src/glob.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* serve-reload — Zero-dependency static file server with live reload.
|
|
3
|
+
*
|
|
4
|
+
* Usage (programmatic):
|
|
5
|
+
*
|
|
6
|
+
* import { ServeReload } from "serve-reload";
|
|
7
|
+
* const server = new ServeReload({ root: "./public", port: 8080 });
|
|
8
|
+
* server.start();
|
|
9
|
+
*/
|
|
10
|
+
export { ServeReload } from "./server.js";
|
|
11
|
+
export type { ServeReloadOptions } from "./server.js";
|
|
12
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAC1C,YAAY,EAAE,kBAAkB,EAAE,MAAM,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* serve-reload — Zero-dependency static file server with live reload.
|
|
4
|
+
*
|
|
5
|
+
* Usage (programmatic):
|
|
6
|
+
*
|
|
7
|
+
* import { ServeReload } from "serve-reload";
|
|
8
|
+
* const server = new ServeReload({ root: "./public", port: 8080 });
|
|
9
|
+
* server.start();
|
|
10
|
+
*/
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.ServeReload = void 0;
|
|
13
|
+
var server_js_1 = require("./server.js");
|
|
14
|
+
Object.defineProperty(exports, "ServeReload", { enumerable: true, get: function () { return server_js_1.ServeReload; } });
|
|
15
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;GAQG;;;AAEH,yCAA0C;AAAjC,wGAAA,WAAW,OAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The tiny client-side script injected into HTML pages.
|
|
3
|
+
* Uses Server-Sent Events (SSE) to listen for reload signals.
|
|
4
|
+
* Automatically reconnects if the connection drops (server restart).
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Inject the live-reload script into an HTML string.
|
|
8
|
+
* Inserts right before </body> if present, otherwise appends to the end.
|
|
9
|
+
*/
|
|
10
|
+
export declare function inject(html: string, sseEndpoint: string): string;
|
|
11
|
+
//# sourceMappingURL=injector.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injector.d.ts","sourceRoot":"","sources":["../src/injector.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAMH;;;GAGG;AACH,wBAAgB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,CAShE"}
|
package/dist/injector.js
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* The tiny client-side script injected into HTML pages.
|
|
4
|
+
* Uses Server-Sent Events (SSE) to listen for reload signals.
|
|
5
|
+
* Automatically reconnects if the connection drops (server restart).
|
|
6
|
+
*/
|
|
7
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
+
exports.inject = inject;
|
|
9
|
+
function clientScript(sseEndpoint) {
|
|
10
|
+
return `<script>/*serve-reload*/(function(){var c=!1;function n(){var e=new EventSource("${sseEndpoint}");e.onmessage=function(ev){var d=ev.data;if(d==="1")return location.reload();var t=Date.now();document.querySelectorAll('link[rel="stylesheet"]').forEach(function(l){var h=l.getAttribute("href");if(h&&h.split("?")[0].endsWith(d)){l.setAttribute("href",h.split("?")[0]+"?t="+t)}})};e.onopen=function(){if(c)location.reload();c=!0};e.onerror=function(){e.close();setTimeout(n,1e3)}}n()})()</script>`;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Inject the live-reload script into an HTML string.
|
|
14
|
+
* Inserts right before </body> if present, otherwise appends to the end.
|
|
15
|
+
*/
|
|
16
|
+
function inject(html, sseEndpoint) {
|
|
17
|
+
const script = clientScript(sseEndpoint);
|
|
18
|
+
if (html.includes("</body>")) {
|
|
19
|
+
return html.replace("</body>", script + "\n</body>");
|
|
20
|
+
}
|
|
21
|
+
if (html.includes("</head>")) {
|
|
22
|
+
return html.replace("</head>", script + "\n</head>");
|
|
23
|
+
}
|
|
24
|
+
return html + script;
|
|
25
|
+
}
|
|
26
|
+
//# sourceMappingURL=injector.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"injector.js","sourceRoot":"","sources":["../src/injector.ts"],"names":[],"mappings":";AAAA;;;;GAIG;;AAUH,wBASC;AAjBD,SAAS,YAAY,CAAC,WAAmB;IACvC,OAAO,oFAAoF,WAAW,+YAA+Y,CAAC;AACxf,CAAC;AAED;;;GAGG;AACH,SAAgB,MAAM,CAAC,IAAY,EAAE,WAAmB;IACtD,MAAM,MAAM,GAAG,YAAY,CAAC,WAAW,CAAC,CAAC;IACzC,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC7B,OAAO,IAAI,CAAC,OAAO,CAAC,SAAS,EAAE,MAAM,GAAG,WAAW,CAAC,CAAC;IACvD,CAAC;IACD,OAAO,IAAI,GAAG,MAAM,CAAC;AACvB,CAAC"}
|
package/dist/mime.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mime.d.ts","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAgDH,wBAAgB,MAAM,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAG/C"}
|
package/dist/mime.js
ADDED
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Minimal MIME type lookup by file extension.
|
|
4
|
+
* Covers the most common static file types served during development.
|
|
5
|
+
*/
|
|
6
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
7
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
8
|
+
};
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
exports.lookup = lookup;
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const MIME_TYPES = {
|
|
13
|
+
// Text / markup
|
|
14
|
+
".html": "text/html; charset=utf-8",
|
|
15
|
+
".htm": "text/html; charset=utf-8",
|
|
16
|
+
".css": "text/css; charset=utf-8",
|
|
17
|
+
".js": "application/javascript; charset=utf-8",
|
|
18
|
+
".mjs": "application/javascript; charset=utf-8",
|
|
19
|
+
".json": "application/json; charset=utf-8",
|
|
20
|
+
".xml": "application/xml; charset=utf-8",
|
|
21
|
+
".txt": "text/plain; charset=utf-8",
|
|
22
|
+
".md": "text/markdown; charset=utf-8",
|
|
23
|
+
".csv": "text/csv; charset=utf-8",
|
|
24
|
+
// Images
|
|
25
|
+
".png": "image/png",
|
|
26
|
+
".jpg": "image/jpeg",
|
|
27
|
+
".jpeg": "image/jpeg",
|
|
28
|
+
".gif": "image/gif",
|
|
29
|
+
".svg": "image/svg+xml",
|
|
30
|
+
".ico": "image/x-icon",
|
|
31
|
+
".webp": "image/webp",
|
|
32
|
+
".avif": "image/avif",
|
|
33
|
+
// Fonts
|
|
34
|
+
".woff": "font/woff",
|
|
35
|
+
".woff2": "font/woff2",
|
|
36
|
+
".ttf": "font/ttf",
|
|
37
|
+
".otf": "font/otf",
|
|
38
|
+
".eot": "application/vnd.ms-fontobject",
|
|
39
|
+
// Media
|
|
40
|
+
".mp4": "video/mp4",
|
|
41
|
+
".webm": "video/webm",
|
|
42
|
+
".ogg": "audio/ogg",
|
|
43
|
+
".mp3": "audio/mpeg",
|
|
44
|
+
".wav": "audio/wav",
|
|
45
|
+
// Other
|
|
46
|
+
".pdf": "application/pdf",
|
|
47
|
+
".zip": "application/zip",
|
|
48
|
+
".wasm": "application/wasm",
|
|
49
|
+
".map": "application/json",
|
|
50
|
+
};
|
|
51
|
+
function lookup(filePath) {
|
|
52
|
+
const ext = node_path_1.default.extname(filePath).toLowerCase();
|
|
53
|
+
return MIME_TYPES[ext] || "application/octet-stream";
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=mime.js.map
|
package/dist/mime.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mime.js","sourceRoot":"","sources":["../src/mime.ts"],"names":[],"mappings":";AAAA;;;GAGG;;;;;AAgDH,wBAGC;AAjDD,0DAA6B;AAE7B,MAAM,UAAU,GAA2B;IACzC,gBAAgB;IAChB,OAAO,EAAE,0BAA0B;IACnC,MAAM,EAAE,0BAA0B;IAClC,MAAM,EAAE,yBAAyB;IACjC,KAAK,EAAE,uCAAuC;IAC9C,MAAM,EAAE,uCAAuC;IAC/C,OAAO,EAAE,iCAAiC;IAC1C,MAAM,EAAE,gCAAgC;IACxC,MAAM,EAAE,2BAA2B;IACnC,KAAK,EAAE,8BAA8B;IACrC,MAAM,EAAE,yBAAyB;IAEjC,SAAS;IACT,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,eAAe;IACvB,MAAM,EAAE,cAAc;IACtB,OAAO,EAAE,YAAY;IACrB,OAAO,EAAE,YAAY;IAErB,QAAQ;IACR,OAAO,EAAE,WAAW;IACpB,QAAQ,EAAE,YAAY;IACtB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,UAAU;IAClB,MAAM,EAAE,+BAA+B;IAEvC,QAAQ;IACR,MAAM,EAAE,WAAW;IACnB,OAAO,EAAE,YAAY;IACrB,MAAM,EAAE,WAAW;IACnB,MAAM,EAAE,YAAY;IACpB,MAAM,EAAE,WAAW;IAEnB,QAAQ;IACR,MAAM,EAAE,iBAAiB;IACzB,MAAM,EAAE,iBAAiB;IACzB,OAAO,EAAE,kBAAkB;IAC3B,MAAM,EAAE,kBAAkB;CAC3B,CAAC;AAEF,SAAgB,MAAM,CAAC,QAAgB;IACrC,MAAM,GAAG,GAAG,mBAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACjD,OAAO,UAAU,CAAC,GAAG,CAAC,IAAI,0BAA0B,CAAC;AACvD,CAAC"}
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Core static file server with SSE-based live reload.
|
|
3
|
+
*/
|
|
4
|
+
import http from "node:http";
|
|
5
|
+
export interface ServeReloadOptions {
|
|
6
|
+
/** Directory to serve (default ".") */
|
|
7
|
+
root?: string;
|
|
8
|
+
/** Port number (default 3000) */
|
|
9
|
+
port?: number;
|
|
10
|
+
/** Host to bind (default "0.0.0.0") */
|
|
11
|
+
host?: string;
|
|
12
|
+
/** Open browser on start (default false) */
|
|
13
|
+
open?: boolean;
|
|
14
|
+
/** Enable live reload (default true) */
|
|
15
|
+
reload?: boolean;
|
|
16
|
+
/** Enable permissive CORS (default true) */
|
|
17
|
+
cors?: boolean;
|
|
18
|
+
/** Suppress request logging (default false) */
|
|
19
|
+
quiet?: boolean;
|
|
20
|
+
/** Extra directory names to ignore when watching */
|
|
21
|
+
ignore?: string[];
|
|
22
|
+
/** Serve this file for 404s — SPA mode (e.g. "index.html") */
|
|
23
|
+
spa?: string | null;
|
|
24
|
+
/** Proxy rules: { route: targetURL } (e.g. { "/api": "https://api.example.com" }) */
|
|
25
|
+
proxy?: Record<string, string>;
|
|
26
|
+
}
|
|
27
|
+
export declare class ServeReload {
|
|
28
|
+
root: string;
|
|
29
|
+
port: number;
|
|
30
|
+
host: string;
|
|
31
|
+
open: boolean;
|
|
32
|
+
reload: boolean;
|
|
33
|
+
cors: boolean;
|
|
34
|
+
quiet: boolean;
|
|
35
|
+
spa: string | null;
|
|
36
|
+
ignore: string[];
|
|
37
|
+
proxy: Map<string, string>;
|
|
38
|
+
private requestedPort;
|
|
39
|
+
private clients;
|
|
40
|
+
private connections;
|
|
41
|
+
_server: http.Server | null;
|
|
42
|
+
private watcher;
|
|
43
|
+
constructor(opts?: ServeReloadOptions);
|
|
44
|
+
start(): this;
|
|
45
|
+
stop(): Promise<void>;
|
|
46
|
+
handleRequest(req: http.IncomingMessage, res: http.ServerResponse): void;
|
|
47
|
+
private serveFile;
|
|
48
|
+
private handle404;
|
|
49
|
+
private handleSSE;
|
|
50
|
+
private broadcast;
|
|
51
|
+
private matchProxy;
|
|
52
|
+
private handleProxy;
|
|
53
|
+
private log;
|
|
54
|
+
private openBrowser;
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=server.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,IAAI,MAAM,WAAW,CAAC;AAa7B,MAAM,WAAW,kBAAkB;IACjC,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iCAAiC;IACjC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,4CAA4C;IAC5C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,wCAAwC;IACxC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,4CAA4C;IAC5C,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,+CAA+C;IAC/C,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,oDAAoD;IACpD,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,qFAAqF;IACrF,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CAChC;AAED,qBAAa,WAAW;IACtB,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,OAAO,CAAC;IACd,MAAM,EAAE,OAAO,CAAC;IAChB,IAAI,EAAE,OAAO,CAAC;IACd,KAAK,EAAE,OAAO,CAAC;IACf,GAAG,EAAE,MAAM,GAAG,IAAI,CAAC;IACnB,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAE3B,OAAO,CAAC,aAAa,CAAS;IAC9B,OAAO,CAAC,OAAO,CAAkC;IACjD,OAAO,CAAC,WAAW,CAAyB;IAE5C,OAAO,EAAE,IAAI,CAAC,MAAM,GAAG,IAAI,CAAQ;IACnC,OAAO,CAAC,OAAO,CAAwB;gBAE3B,IAAI,GAAE,kBAAuB;IAczC,KAAK,IAAI,IAAI;IAmDP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;IAqB3B,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,eAAe,EAAE,GAAG,EAAE,IAAI,CAAC,cAAc,GAAG,IAAI;IA6CxE,OAAO,CAAC,SAAS;IAuDjB,OAAO,CAAC,SAAS;IAsBjB,OAAO,CAAC,SAAS;IAcjB,OAAO,CAAC,SAAS;IAUjB,OAAO,CAAC,UAAU;IAWlB,OAAO,CAAC,WAAW;IAoCnB,OAAO,CAAC,GAAG;IAOX,OAAO,CAAC,WAAW;CASpB"}
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,286 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Core static file server with SSE-based live reload.
|
|
4
|
+
*/
|
|
5
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
6
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
7
|
+
};
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.ServeReload = void 0;
|
|
10
|
+
const node_http_1 = __importDefault(require("node:http"));
|
|
11
|
+
const node_https_1 = __importDefault(require("node:https"));
|
|
12
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
13
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
14
|
+
const node_url_1 = require("node:url");
|
|
15
|
+
const node_child_process_1 = require("node:child_process");
|
|
16
|
+
const mime_js_1 = require("./mime.js");
|
|
17
|
+
const injector_js_1 = require("./injector.js");
|
|
18
|
+
const watcher_js_1 = require("./watcher.js");
|
|
19
|
+
const SSE_ENDPOINT = "/__serve_reload_sse__";
|
|
20
|
+
class ServeReload {
|
|
21
|
+
root;
|
|
22
|
+
port;
|
|
23
|
+
host;
|
|
24
|
+
open;
|
|
25
|
+
reload;
|
|
26
|
+
cors;
|
|
27
|
+
quiet;
|
|
28
|
+
spa;
|
|
29
|
+
ignore;
|
|
30
|
+
proxy;
|
|
31
|
+
requestedPort;
|
|
32
|
+
clients = new Set();
|
|
33
|
+
connections = new Set();
|
|
34
|
+
/* @internal – exposed for testing */
|
|
35
|
+
_server = null;
|
|
36
|
+
watcher = null;
|
|
37
|
+
constructor(opts = {}) {
|
|
38
|
+
this.root = node_path_1.default.resolve(opts.root ?? ".");
|
|
39
|
+
this.port = opts.port ?? 3000;
|
|
40
|
+
this.host = opts.host ?? "0.0.0.0";
|
|
41
|
+
this.open = opts.open ?? false;
|
|
42
|
+
this.reload = opts.reload ?? true;
|
|
43
|
+
this.cors = opts.cors ?? true;
|
|
44
|
+
this.quiet = opts.quiet ?? false;
|
|
45
|
+
this.spa = opts.spa ?? null;
|
|
46
|
+
this.ignore = ["node_modules", ".git", ".DS_Store", ...(opts.ignore ?? [])];
|
|
47
|
+
this.proxy = new Map(Object.entries(opts.proxy ?? {}));
|
|
48
|
+
this.requestedPort = this.port;
|
|
49
|
+
}
|
|
50
|
+
start() {
|
|
51
|
+
this._server = node_http_1.default.createServer((req, res) => this.handleRequest(req, res));
|
|
52
|
+
// Track open connections so we can destroy them on stop
|
|
53
|
+
this._server.on("connection", (socket) => {
|
|
54
|
+
this.connections.add(socket);
|
|
55
|
+
socket.on("close", () => this.connections.delete(socket));
|
|
56
|
+
});
|
|
57
|
+
this._server.on("error", (err) => {
|
|
58
|
+
if (err.code === "EADDRINUSE" && this.port === this.requestedPort) {
|
|
59
|
+
console.log(` Port ${this.port} is busy, using a random port...`);
|
|
60
|
+
this.port = 0;
|
|
61
|
+
this._server.listen(0, this.host);
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
throw err;
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
this._server.listen(this.port, this.host, () => {
|
|
68
|
+
const addr = this._server.address();
|
|
69
|
+
this.port = addr.port;
|
|
70
|
+
const url = `http://localhost:${this.port}`;
|
|
71
|
+
console.log(`\n serve-reload\n`);
|
|
72
|
+
console.log(` Serving ${this.root}`);
|
|
73
|
+
console.log(` Local ${url}`);
|
|
74
|
+
if (this.reload)
|
|
75
|
+
console.log(` Reload enabled`);
|
|
76
|
+
if (this.spa)
|
|
77
|
+
console.log(` SPA fallback → ${this.spa}`);
|
|
78
|
+
for (const [route, target] of this.proxy) {
|
|
79
|
+
console.log(` Proxy ${route} → ${target}`);
|
|
80
|
+
}
|
|
81
|
+
console.log();
|
|
82
|
+
if (this.open)
|
|
83
|
+
this.openBrowser(url);
|
|
84
|
+
});
|
|
85
|
+
if (this.reload) {
|
|
86
|
+
this.watcher = new watcher_js_1.Watcher(this.root, {
|
|
87
|
+
ignore: this.ignore,
|
|
88
|
+
onChange: (filePath) => {
|
|
89
|
+
const isCSS = filePath.endsWith(".css");
|
|
90
|
+
if (!this.quiet)
|
|
91
|
+
console.log(` ${isCSS ? "css" : "changed"} ${filePath}`);
|
|
92
|
+
this.broadcast(filePath);
|
|
93
|
+
},
|
|
94
|
+
});
|
|
95
|
+
this.watcher.start();
|
|
96
|
+
}
|
|
97
|
+
return this;
|
|
98
|
+
}
|
|
99
|
+
async stop() {
|
|
100
|
+
if (this.watcher)
|
|
101
|
+
this.watcher.stop();
|
|
102
|
+
for (const res of this.clients) {
|
|
103
|
+
res.end();
|
|
104
|
+
}
|
|
105
|
+
this.clients.clear();
|
|
106
|
+
// Destroy all open sockets so server.close() doesn't hang
|
|
107
|
+
for (const socket of this.connections) {
|
|
108
|
+
socket.destroy();
|
|
109
|
+
}
|
|
110
|
+
this.connections.clear();
|
|
111
|
+
if (this._server) {
|
|
112
|
+
await new Promise((resolve) => this._server.close(() => resolve()));
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
/* ---- request handling ---- */
|
|
116
|
+
handleRequest(req, res) {
|
|
117
|
+
// SSE endpoint
|
|
118
|
+
if (req.url === SSE_ENDPOINT) {
|
|
119
|
+
return this.handleSSE(req, res);
|
|
120
|
+
}
|
|
121
|
+
// Proxy
|
|
122
|
+
const proxyTarget = this.matchProxy(req.url);
|
|
123
|
+
if (proxyTarget) {
|
|
124
|
+
return this.handleProxy(req, res, proxyTarget.target, proxyTarget.rewrittenPath);
|
|
125
|
+
}
|
|
126
|
+
// CORS headers
|
|
127
|
+
if (this.cors) {
|
|
128
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
129
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, HEAD, OPTIONS");
|
|
130
|
+
res.setHeader("Access-Control-Allow-Headers", "*");
|
|
131
|
+
if (req.method === "OPTIONS") {
|
|
132
|
+
res.writeHead(204);
|
|
133
|
+
return void res.end();
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
// Only GET / HEAD
|
|
137
|
+
if (req.method !== "GET" && req.method !== "HEAD") {
|
|
138
|
+
res.writeHead(405, { "Content-Type": "text/plain" });
|
|
139
|
+
return void res.end("Method Not Allowed");
|
|
140
|
+
}
|
|
141
|
+
// Decode & sanitize URL
|
|
142
|
+
let urlPath;
|
|
143
|
+
try {
|
|
144
|
+
urlPath = decodeURIComponent(req.url.split("?")[0]);
|
|
145
|
+
}
|
|
146
|
+
catch {
|
|
147
|
+
res.writeHead(400);
|
|
148
|
+
return void res.end("Bad Request");
|
|
149
|
+
}
|
|
150
|
+
// Prevent path traversal
|
|
151
|
+
const safePath = node_path_1.default.normalize(urlPath).replace(/^(\.\.[/\\])+/, "");
|
|
152
|
+
const filePath = node_path_1.default.join(this.root, safePath);
|
|
153
|
+
this.serveFile(req, res, filePath, urlPath);
|
|
154
|
+
}
|
|
155
|
+
serveFile(req, res, filePath, urlPath) {
|
|
156
|
+
node_fs_1.default.stat(filePath, (err, stats) => {
|
|
157
|
+
if (err || !stats || (!stats.isFile() && !stats.isDirectory())) {
|
|
158
|
+
return this.handle404(req, res, urlPath);
|
|
159
|
+
}
|
|
160
|
+
// Directory → try index.html
|
|
161
|
+
if (stats.isDirectory()) {
|
|
162
|
+
if (!urlPath.endsWith("/")) {
|
|
163
|
+
res.writeHead(301, { Location: urlPath + "/" });
|
|
164
|
+
return void res.end();
|
|
165
|
+
}
|
|
166
|
+
return this.serveFile(req, res, node_path_1.default.join(filePath, "index.html"), urlPath);
|
|
167
|
+
}
|
|
168
|
+
const mimeType = (0, mime_js_1.lookup)(filePath);
|
|
169
|
+
const isHTML = mimeType.startsWith("text/html");
|
|
170
|
+
if (isHTML && this.reload) {
|
|
171
|
+
// Read entirely to inject script
|
|
172
|
+
node_fs_1.default.readFile(filePath, "utf-8", (readErr, content) => {
|
|
173
|
+
if (readErr) {
|
|
174
|
+
res.writeHead(500);
|
|
175
|
+
return void res.end("Internal Server Error");
|
|
176
|
+
}
|
|
177
|
+
const injected = (0, injector_js_1.inject)(content, SSE_ENDPOINT);
|
|
178
|
+
const buf = Buffer.from(injected, "utf-8");
|
|
179
|
+
res.writeHead(200, {
|
|
180
|
+
"Content-Type": mimeType,
|
|
181
|
+
"Content-Length": buf.length,
|
|
182
|
+
"Cache-Control": "no-cache, no-store, must-revalidate",
|
|
183
|
+
});
|
|
184
|
+
if (req.method === "HEAD")
|
|
185
|
+
return void res.end();
|
|
186
|
+
res.end(buf);
|
|
187
|
+
this.log(200, urlPath);
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
// Stream the file
|
|
192
|
+
res.writeHead(200, {
|
|
193
|
+
"Content-Type": mimeType,
|
|
194
|
+
"Content-Length": stats.size,
|
|
195
|
+
"Cache-Control": "no-cache",
|
|
196
|
+
});
|
|
197
|
+
if (req.method === "HEAD")
|
|
198
|
+
return void res.end();
|
|
199
|
+
node_fs_1.default.createReadStream(filePath).pipe(res);
|
|
200
|
+
this.log(200, urlPath);
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
handle404(req, res, urlPath) {
|
|
205
|
+
// SPA fallback
|
|
206
|
+
if (this.spa) {
|
|
207
|
+
const spaPath = node_path_1.default.join(this.root, this.spa);
|
|
208
|
+
if (node_fs_1.default.existsSync(spaPath)) {
|
|
209
|
+
return this.serveFile(req, res, spaPath, urlPath);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
res.writeHead(404, { "Content-Type": "text/html; charset=utf-8" });
|
|
213
|
+
res.end(`<!DOCTYPE html><html><head><title>404</title></head><body><h1>404 — Not Found</h1><p>${urlPath}</p></body></html>`);
|
|
214
|
+
this.log(404, urlPath);
|
|
215
|
+
}
|
|
216
|
+
/* ---- SSE ---- */
|
|
217
|
+
handleSSE(_req, res) {
|
|
218
|
+
res.writeHead(200, {
|
|
219
|
+
"Content-Type": "text/event-stream",
|
|
220
|
+
"Cache-Control": "no-cache",
|
|
221
|
+
Connection: "keep-alive",
|
|
222
|
+
});
|
|
223
|
+
res.write(":\n\n"); // comment to keep alive
|
|
224
|
+
this.clients.add(res);
|
|
225
|
+
_req.on("close", () => {
|
|
226
|
+
this.clients.delete(res);
|
|
227
|
+
});
|
|
228
|
+
}
|
|
229
|
+
broadcast(filePath) {
|
|
230
|
+
const isCSS = filePath.endsWith(".css");
|
|
231
|
+
const msg = isCSS ? `data: ${filePath}\n\n` : `data: 1\n\n`;
|
|
232
|
+
for (const client of this.clients) {
|
|
233
|
+
client.write(msg);
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
/* ---- proxy ---- */
|
|
237
|
+
matchProxy(url) {
|
|
238
|
+
const urlPath = url.split("?")[0];
|
|
239
|
+
for (const [route, target] of this.proxy) {
|
|
240
|
+
if (urlPath === route || urlPath.startsWith(route + "/") || urlPath.startsWith(route + "?")) {
|
|
241
|
+
// Keep the full original URL (path + query) for forwarding
|
|
242
|
+
return { target, rewrittenPath: url };
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
return null;
|
|
246
|
+
}
|
|
247
|
+
handleProxy(req, res, target, forwardPath) {
|
|
248
|
+
const targetUrl = new node_url_1.URL(forwardPath, target);
|
|
249
|
+
const lib = targetUrl.protocol === "https:" ? node_https_1.default : node_http_1.default;
|
|
250
|
+
const proxyReq = lib.request(targetUrl, {
|
|
251
|
+
method: req.method,
|
|
252
|
+
headers: {
|
|
253
|
+
...req.headers,
|
|
254
|
+
host: targetUrl.host,
|
|
255
|
+
},
|
|
256
|
+
}, (proxyRes) => {
|
|
257
|
+
res.writeHead(proxyRes.statusCode ?? 502, proxyRes.headers);
|
|
258
|
+
proxyRes.pipe(res);
|
|
259
|
+
this.log(proxyRes.statusCode ?? 502, `⇄ ${forwardPath}`);
|
|
260
|
+
});
|
|
261
|
+
proxyReq.on("error", (err) => {
|
|
262
|
+
res.writeHead(502, { "Content-Type": "text/plain" });
|
|
263
|
+
res.end(`Proxy error: ${err.message}`);
|
|
264
|
+
this.log(502, `⇄ ${forwardPath}`);
|
|
265
|
+
});
|
|
266
|
+
req.pipe(proxyReq);
|
|
267
|
+
}
|
|
268
|
+
/* ---- helpers ---- */
|
|
269
|
+
log(status, urlPath) {
|
|
270
|
+
if (this.quiet)
|
|
271
|
+
return;
|
|
272
|
+
const color = status < 400 ? "\x1b[32m" : "\x1b[33m";
|
|
273
|
+
const reset = "\x1b[0m";
|
|
274
|
+
console.log(` ${color}${status}${reset} ${urlPath}`);
|
|
275
|
+
}
|
|
276
|
+
openBrowser(url) {
|
|
277
|
+
const cmd = process.platform === "darwin"
|
|
278
|
+
? "open"
|
|
279
|
+
: process.platform === "win32"
|
|
280
|
+
? "start"
|
|
281
|
+
: "xdg-open";
|
|
282
|
+
(0, node_child_process_1.exec)(`${cmd} ${url}`);
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
exports.ServeReload = ServeReload;
|
|
286
|
+
//# sourceMappingURL=server.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"server.js","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* File watcher built on top of fs.watch (recursive mode).
|
|
3
|
+
*
|
|
4
|
+
* fs.watch with { recursive: true } is supported on macOS and Windows.
|
|
5
|
+
* On Linux (kernel ≥ 5.9, Node ≥ 19.1) it is also supported.
|
|
6
|
+
* For older Linux, we fall back to walking the directory tree and
|
|
7
|
+
* watching each subdirectory individually.
|
|
8
|
+
*/
|
|
9
|
+
export interface WatcherOptions {
|
|
10
|
+
/** Patterns to ignore (directory names, globs, paths). e.g. ["node_modules", "*.map", "src/temp"] */
|
|
11
|
+
ignore?: string[];
|
|
12
|
+
/** Milliseconds to debounce rapid changes (default 150) */
|
|
13
|
+
debounce?: number;
|
|
14
|
+
/** Callback invoked with the relative file path that changed */
|
|
15
|
+
onChange?: (filePath: string) => void;
|
|
16
|
+
}
|
|
17
|
+
export declare class Watcher {
|
|
18
|
+
readonly root: string;
|
|
19
|
+
private readonly ignoreSet;
|
|
20
|
+
private readonly isIgnored;
|
|
21
|
+
private readonly debounce;
|
|
22
|
+
private readonly onChange;
|
|
23
|
+
private watchers;
|
|
24
|
+
private timers;
|
|
25
|
+
constructor(root: string, opts?: WatcherOptions);
|
|
26
|
+
start(): void;
|
|
27
|
+
stop(): void;
|
|
28
|
+
private tryRecursive;
|
|
29
|
+
private walkAndWatch;
|
|
30
|
+
private handle;
|
|
31
|
+
}
|
|
32
|
+
//# sourceMappingURL=watcher.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.d.ts","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAMH,MAAM,WAAW,cAAc;IAC7B,qGAAqG;IACrG,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;IAClB,2DAA2D;IAC3D,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,gEAAgE;IAChE,QAAQ,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAC;CACvC;AAED,qBAAa,OAAO;IAClB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAc;IACxC,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAgC;IAC1D,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAAS;IAClC,OAAO,CAAC,QAAQ,CAAC,QAAQ,CAA6B;IACtD,OAAO,CAAC,QAAQ,CAAsB;IACtC,OAAO,CAAC,MAAM,CAAoD;gBAEtD,IAAI,EAAE,MAAM,EAAE,IAAI,GAAE,cAAmB;IAWnD,KAAK,IAAI,IAAI;IAQb,IAAI,IAAI,IAAI;IAaZ,OAAO,CAAC,YAAY;IAOpB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,MAAM;CAef"}
|
package/dist/watcher.js
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* File watcher built on top of fs.watch (recursive mode).
|
|
4
|
+
*
|
|
5
|
+
* fs.watch with { recursive: true } is supported on macOS and Windows.
|
|
6
|
+
* On Linux (kernel ≥ 5.9, Node ≥ 19.1) it is also supported.
|
|
7
|
+
* For older Linux, we fall back to walking the directory tree and
|
|
8
|
+
* watching each subdirectory individually.
|
|
9
|
+
*/
|
|
10
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
11
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
12
|
+
};
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.Watcher = void 0;
|
|
15
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
16
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
17
|
+
const glob_js_1 = require("./glob.js");
|
|
18
|
+
class Watcher {
|
|
19
|
+
root;
|
|
20
|
+
ignoreSet;
|
|
21
|
+
isIgnored;
|
|
22
|
+
debounce;
|
|
23
|
+
onChange;
|
|
24
|
+
watchers = [];
|
|
25
|
+
timers = new Map();
|
|
26
|
+
constructor(root, opts = {}) {
|
|
27
|
+
this.root = node_path_1.default.resolve(root);
|
|
28
|
+
const patterns = opts.ignore ?? ["node_modules", ".git", ".DS_Store"];
|
|
29
|
+
// Simple names go into a Set for fast directory-walk filtering
|
|
30
|
+
this.ignoreSet = new Set(patterns.filter((p) => !p.includes("*") && !p.includes("/") && !p.includes("?")));
|
|
31
|
+
// Full glob matcher for all patterns
|
|
32
|
+
this.isIgnored = (0, glob_js_1.createMatcher)(patterns);
|
|
33
|
+
this.debounce = opts.debounce ?? 150;
|
|
34
|
+
this.onChange = opts.onChange ?? (() => { });
|
|
35
|
+
}
|
|
36
|
+
start() {
|
|
37
|
+
try {
|
|
38
|
+
this.tryRecursive();
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
this.walkAndWatch(this.root);
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
stop() {
|
|
45
|
+
for (const w of this.watchers) {
|
|
46
|
+
w.close();
|
|
47
|
+
}
|
|
48
|
+
this.watchers = [];
|
|
49
|
+
for (const t of this.timers.values()) {
|
|
50
|
+
clearTimeout(t);
|
|
51
|
+
}
|
|
52
|
+
this.timers.clear();
|
|
53
|
+
}
|
|
54
|
+
/* ---- internals ---- */
|
|
55
|
+
tryRecursive() {
|
|
56
|
+
const w = node_fs_1.default.watch(this.root, { recursive: true }, (_, filename) => {
|
|
57
|
+
if (filename)
|
|
58
|
+
this.handle(filename);
|
|
59
|
+
});
|
|
60
|
+
this.watchers.push(w);
|
|
61
|
+
}
|
|
62
|
+
walkAndWatch(dir) {
|
|
63
|
+
if (this.ignoreSet.has(node_path_1.default.basename(dir)))
|
|
64
|
+
return;
|
|
65
|
+
const w = node_fs_1.default.watch(dir, (_, filename) => {
|
|
66
|
+
if (filename)
|
|
67
|
+
this.handle(node_path_1.default.relative(this.root, node_path_1.default.join(dir, filename)));
|
|
68
|
+
});
|
|
69
|
+
this.watchers.push(w);
|
|
70
|
+
for (const entry of node_fs_1.default.readdirSync(dir, { withFileTypes: true })) {
|
|
71
|
+
if (entry.isDirectory() && !this.ignoreSet.has(entry.name)) {
|
|
72
|
+
this.walkAndWatch(node_path_1.default.join(dir, entry.name));
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
handle(relative) {
|
|
77
|
+
if (this.isIgnored(relative))
|
|
78
|
+
return;
|
|
79
|
+
// debounce per file
|
|
80
|
+
const existing = this.timers.get(relative);
|
|
81
|
+
if (existing)
|
|
82
|
+
clearTimeout(existing);
|
|
83
|
+
this.timers.set(relative, setTimeout(() => {
|
|
84
|
+
this.timers.delete(relative);
|
|
85
|
+
this.onChange(relative);
|
|
86
|
+
}, this.debounce));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
exports.Watcher = Watcher;
|
|
90
|
+
//# sourceMappingURL=watcher.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"watcher.js","sourceRoot":"","sources":["../src/watcher.ts"],"names":[],"mappings":"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"}
|
package/package.json
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "serve-reload",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Zero-dependency static file server with live reload for development",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"types": "dist/index.d.ts",
|
|
7
|
+
"bin": {
|
|
8
|
+
"serve-reload": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"build": "tsc",
|
|
12
|
+
"dev": "tsc --watch & node --watch-path=dist dist/cli.js",
|
|
13
|
+
"start": "node dist/cli.js",
|
|
14
|
+
"test": "node test/test.js && node test/glob.test.js",
|
|
15
|
+
"prepublishOnly": "tsc && npm test"
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"static",
|
|
19
|
+
"server",
|
|
20
|
+
"live-reload",
|
|
21
|
+
"dev-server",
|
|
22
|
+
"zero-dependency"
|
|
23
|
+
],
|
|
24
|
+
"license": "MIT",
|
|
25
|
+
"engines": {
|
|
26
|
+
"node": ">=18"
|
|
27
|
+
},
|
|
28
|
+
"devDependencies": {
|
|
29
|
+
"@types/node": "^25.5.0",
|
|
30
|
+
"typescript": "^5.8.0"
|
|
31
|
+
},
|
|
32
|
+
"files": [
|
|
33
|
+
"dist/",
|
|
34
|
+
"README.md"
|
|
35
|
+
]
|
|
36
|
+
}
|