@caboodle-tech/node-simple-server 4.2.3 → 4.2.7
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/README.md +23 -16
- package/bin/nss.js +60 -45
- package/bin/print.js +0 -2
- package/changelogs/v4.md +28 -0
- package/eslint/html-rules.js +24 -0
- package/eslint/js-rules.js +143 -0
- package/eslint/json-rules.js +21 -0
- package/eslint.config.js +44 -0
- package/examples/controllers/prod-website.js +3 -0
- package/examples/controllers/website.js +3 -0
- package/examples/controllers/websocket.js +3 -0
- package/handlers/dir-listing.html +9 -9
- package/handlers/forbidden.html +5 -5
- package/handlers/live-reloading.html +1 -1
- package/handlers/not-found.html +5 -5
- package/handlers/websocket-only.html +1 -1
- package/package.json +11 -6
- package/.eslintrc.json +0 -50
package/README.md
CHANGED
|
@@ -16,36 +16,42 @@ A small but effective node based server for development sites, customizable live
|
|
|
16
16
|
|
|
17
17
|
## Installation
|
|
18
18
|
|
|
19
|
-
### Manually:
|
|
20
|
-
|
|
21
|
-
Node Simple Server (NSS) can be manually incorporated into your development process/ application. Extract the `nss` folder from the [latest release](https://github.com/caboodle-tech/node-simple-server/releases/) and then `import` the server module into your code, similar to:
|
|
22
|
-
|
|
23
|
-
```javascript
|
|
24
|
-
import NodeSimpleServer from './nss.js';
|
|
25
|
-
```
|
|
26
|
-
|
|
27
19
|
### Locally:
|
|
28
20
|
|
|
29
|
-
|
|
21
|
+
Like any other Node based application you can install NSS automatically with `pnpm` or `npm` and then reference it in your code. This is the recommended way to install and use NSS.
|
|
30
22
|
|
|
31
23
|
```bash
|
|
32
|
-
#
|
|
24
|
+
# Install using pnpm:
|
|
25
|
+
pnpm add @caboodle-tech/node-simple-server
|
|
26
|
+
|
|
27
|
+
# Or install using npm:
|
|
33
28
|
npm install @caboodle-tech/node-simple-server
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Then use in your code with:
|
|
34
32
|
|
|
35
|
-
|
|
36
|
-
|
|
33
|
+
```javascript
|
|
34
|
+
import NodeSimpleServer from '@caboodle-tech/node-simple-server';
|
|
37
35
|
```
|
|
38
36
|
|
|
39
|
-
|
|
37
|
+
### Manually:
|
|
38
|
+
|
|
39
|
+
Node Simple Server (NSS) can be manually incorporated into your development process/ application. Extract the `nss` folder from the [latest release](https://github.com/caboodle-tech/node-simple-server/releases/) and then `import` the server module into your code, similar to:
|
|
40
|
+
|
|
41
|
+
```javascript
|
|
42
|
+
import NodeSimpleServer from './nss.js';
|
|
43
|
+
```
|
|
40
44
|
|
|
41
45
|
### Globally:
|
|
42
46
|
|
|
43
47
|
You can install and use NSS globally with:
|
|
44
48
|
|
|
45
49
|
```bash
|
|
46
|
-
|
|
50
|
+
pnpm install -g @caboodle-tech/node-simple-server
|
|
47
51
|
```
|
|
48
52
|
|
|
53
|
+
Depending on how you use and incorporate NSS into your project will determine the best dependency strategy to use.
|
|
54
|
+
|
|
49
55
|
## Usage
|
|
50
56
|
|
|
51
57
|
NSS is designed to be controlled and/or wrapped by another application. The bare minimum code needed to use NSS in your application is:
|
|
@@ -78,7 +84,8 @@ const serverOptions = {
|
|
|
78
84
|
const Server = new NodeSimpleServer(serverOptions);
|
|
79
85
|
|
|
80
86
|
// A bare minimum callback to handle most development changes.
|
|
81
|
-
function watcherCallback(event, path,
|
|
87
|
+
function watcherCallback(event, path, statsOrDetails) {
|
|
88
|
+
const extension = statsOrDetails.ext;
|
|
82
89
|
if (extension === 'css') {
|
|
83
90
|
Server.reloadAllStyles();
|
|
84
91
|
return;
|
|
@@ -283,7 +290,7 @@ With your new instance of NSS you can call any of the following public methods:
|
|
|
283
290
|
|
|
284
291
|
- Send a message (`msg`) via WebSocket to the page that matches the `pageId`, or send to a page or pages that match the `pattern`.
|
|
285
292
|
|
|
286
|
-
### **printListeningAddresses(
|
|
293
|
+
### **printListeningAddresses(\[returnInstead = false\])**
|
|
287
294
|
|
|
288
295
|
- Prints a message to console with all the addresses the server is available at. If you want full control of the printing you can set `returnInstead` to `true`.
|
|
289
296
|
|
package/bin/nss.js
CHANGED
|
@@ -10,9 +10,8 @@ import ContentTypes from '../handlers/js/content-types.js';
|
|
|
10
10
|
import HTTPStatus from '../handlers/js/http-status.js';
|
|
11
11
|
import Print from './print.js';
|
|
12
12
|
|
|
13
|
-
// eslint-disable-next-line no-underscore-dangle
|
|
14
13
|
const __filename = fileURLToPath(import.meta.url);
|
|
15
|
-
|
|
14
|
+
|
|
16
15
|
const __dirname = Path.dirname(__filename);
|
|
17
16
|
const APP_ROOT = Path.join(__dirname, '../');
|
|
18
17
|
|
|
@@ -44,7 +43,7 @@ class NodeSimpleServer {
|
|
|
44
43
|
map: {}
|
|
45
44
|
};
|
|
46
45
|
|
|
47
|
-
#VERSION = '4.2.
|
|
46
|
+
#VERSION = '4.2.7';
|
|
48
47
|
|
|
49
48
|
#watching = [];
|
|
50
49
|
|
|
@@ -125,16 +124,23 @@ class NodeSimpleServer {
|
|
|
125
124
|
* Get an array of all the IP addresses you can reach this server at either from
|
|
126
125
|
* the machine itself or on the LAN.
|
|
127
126
|
*
|
|
127
|
+
* @param {number} [port] The port number to use in the addresses. If not provided,
|
|
128
|
+
* will use the port currently in use by the server, or the
|
|
129
|
+
* configured port if the server hasn't started yet.
|
|
128
130
|
* @return {Array} An array of loop back ip addresses and LAN addresses to this server.
|
|
129
131
|
*/
|
|
130
132
|
getAddresses(port) {
|
|
133
|
+
// Use provided port, or fall back to portInUse (actual port server is using),
|
|
134
|
+
// or finally to the configured port
|
|
135
|
+
// Check for undefined/null specifically to allow 0 if explicitly provided (though unlikely)
|
|
136
|
+
const actualPort = port !== undefined && port !== null ? port : this.#OPS.portInUse || this.#OPS.port;
|
|
131
137
|
const locals = this.#getLocalAddresses();
|
|
132
138
|
const addresses = [
|
|
133
|
-
`http://localhost:${
|
|
134
|
-
`http://127.0.0.1:${
|
|
139
|
+
`http://localhost:${actualPort}`,
|
|
140
|
+
`http://127.0.0.1:${actualPort}`
|
|
135
141
|
];
|
|
136
142
|
Object.keys(locals).forEach((key) => {
|
|
137
|
-
addresses.push(`http://${locals[key]}:${
|
|
143
|
+
addresses.push(`http://${locals[key]}:${actualPort}`);
|
|
138
144
|
});
|
|
139
145
|
return addresses;
|
|
140
146
|
}
|
|
@@ -156,8 +162,8 @@ class NodeSimpleServer {
|
|
|
156
162
|
files.push(item);
|
|
157
163
|
}
|
|
158
164
|
});
|
|
159
|
-
files.sort((a, b) => a.localeCompare(b));
|
|
160
|
-
dirs.sort((a, b) => a.localeCompare(b));
|
|
165
|
+
files.sort((a, b) => { return a.localeCompare(b); });
|
|
166
|
+
dirs.sort((a, b) => { return a.localeCompare(b); });
|
|
161
167
|
// Build the innerHTML for the directory and files unordered list.
|
|
162
168
|
let fileHtml = '';
|
|
163
169
|
let dirHtml = '';
|
|
@@ -222,18 +228,11 @@ class NodeSimpleServer {
|
|
|
222
228
|
let hour = timestamp.getUTCHours();
|
|
223
229
|
let minute = timestamp.getUTCMinutes();
|
|
224
230
|
let second = timestamp.getUTCSeconds();
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
if (minute.length < 10) {
|
|
232
|
-
minute = `0${minute}`;
|
|
233
|
-
}
|
|
234
|
-
if (second.length < 10) {
|
|
235
|
-
second = `0${second}`;
|
|
236
|
-
}
|
|
231
|
+
// Convert to string and pad with leading zero if needed
|
|
232
|
+
dayNum = String(dayNum).padStart(2, '0');
|
|
233
|
+
hour = String(hour).padStart(2, '0');
|
|
234
|
+
minute = String(minute).padStart(2, '0');
|
|
235
|
+
second = String(second).padStart(2, '0');
|
|
237
236
|
return `${dayStr}, ${dayNum} ${monStr} ${timestamp.getUTCFullYear()} ${hour}:${minute}:${second} GMT`;
|
|
238
237
|
}
|
|
239
238
|
|
|
@@ -288,7 +287,7 @@ class NodeSimpleServer {
|
|
|
288
287
|
// Standard headers that should always be set for NSS.
|
|
289
288
|
let mtime = new Date().toUTCString();
|
|
290
289
|
if (settings?.file) {
|
|
291
|
-
mtime = Fs.statSync(settings.file)
|
|
290
|
+
({ mtime } = Fs.statSync(settings.file));
|
|
292
291
|
}
|
|
293
292
|
const nssHeaders = {
|
|
294
293
|
'Cache-Control': 'public, max-age=0',
|
|
@@ -324,7 +323,7 @@ class NodeSimpleServer {
|
|
|
324
323
|
* like directory listing, page not found, access denied, and so on.
|
|
325
324
|
*/
|
|
326
325
|
#loadHandlers() {
|
|
327
|
-
// eslint-disable-next-line max-len
|
|
326
|
+
// eslint-disable-next-line max-len
|
|
328
327
|
const internalError = '<!DOCTYPE html><html><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>500 Internal Server Error</title><style>body,html{margin:0;padding:0}body{padding:15px}</style></head><body><h1>500 Internal Server Error</h1><p>Could not locate source file.</p><hr><p><i>Node Simple Server (NSS) {{version}} Server <script type="text/javascript">document.write(`${document.location.protocol}//${document.location.hostname}`);</script> Port <script type="text/javascript">document.write(document.location.port)</script></i></p>{{liveReloading}}</body></html>';
|
|
329
328
|
|
|
330
329
|
const dirListingSrc = Path.join(APP_ROOT, 'handlers', 'dir-listing.html');
|
|
@@ -377,18 +376,32 @@ class NodeSimpleServer {
|
|
|
377
376
|
* Print the addresses the server is listening on to the console; this is useful for users who
|
|
378
377
|
* are not sure what address to use to access the server.
|
|
379
378
|
*
|
|
380
|
-
* @param {
|
|
379
|
+
* @param {number|boolean} [portOrReturn] If a number, the port to use in the addresses.
|
|
380
|
+
* If a boolean, whether to return the message instead of printing.
|
|
381
381
|
* @param {boolean} [returnInstead=false] If true the function will return the message string instead.
|
|
382
382
|
*/
|
|
383
|
-
|
|
384
|
-
printListeningAddresses(
|
|
383
|
+
|
|
384
|
+
printListeningAddresses(portOrReturn, returnInstead = false) {
|
|
385
|
+
// Handle backward compatibility: if first arg is boolean, it's returnInstead
|
|
386
|
+
let port;
|
|
387
|
+
let shouldReturn = returnInstead;
|
|
388
|
+
if (typeof portOrReturn === 'boolean') {
|
|
389
|
+
shouldReturn = portOrReturn;
|
|
390
|
+
port = undefined;
|
|
391
|
+
} else if (typeof portOrReturn === 'number') {
|
|
392
|
+
port = portOrReturn;
|
|
393
|
+
} else {
|
|
394
|
+
port = undefined;
|
|
395
|
+
}
|
|
396
|
+
|
|
385
397
|
let message = 'Node Simple Server live @:\n';
|
|
386
398
|
const addresses = this.getAddresses(port);
|
|
387
399
|
addresses.forEach((address) => {
|
|
388
|
-
message += ` ${address}`;
|
|
400
|
+
message += ` ${address}\n`;
|
|
389
401
|
});
|
|
402
|
+
message += '\n';
|
|
390
403
|
|
|
391
|
-
if (
|
|
404
|
+
if (shouldReturn) {
|
|
392
405
|
return message;
|
|
393
406
|
}
|
|
394
407
|
Print.notice(message);
|
|
@@ -408,7 +421,7 @@ class NodeSimpleServer {
|
|
|
408
421
|
}
|
|
409
422
|
if (pattern[0] === '/' && pattern[pattern.length - 1] === '/') {
|
|
410
423
|
// eslint-disable-next-line no-param-reassign
|
|
411
|
-
pattern = pattern.
|
|
424
|
+
pattern = pattern.slice(1, -1);
|
|
412
425
|
}
|
|
413
426
|
return new RegExp(`^${pattern}$`);
|
|
414
427
|
} catch (e) {
|
|
@@ -753,7 +766,8 @@ class NodeSimpleServer {
|
|
|
753
766
|
*/
|
|
754
767
|
#socketListener(socket, request) {
|
|
755
768
|
// Strip the page ID and /ws tag off the url to get the actual url.
|
|
756
|
-
|
|
769
|
+
const wsIndex = request.url.indexOf('/ws?id=');
|
|
770
|
+
let cleanURL = wsIndex > 0 ? request.url.slice(1, wsIndex) : '';
|
|
757
771
|
if (!cleanURL) {
|
|
758
772
|
cleanURL = this.#OPS.indexPage;
|
|
759
773
|
}
|
|
@@ -779,17 +793,16 @@ class NodeSimpleServer {
|
|
|
779
793
|
|
|
780
794
|
let pageId;
|
|
781
795
|
if (idStartIndex !== -1) {
|
|
782
|
-
pageId = request.url.
|
|
796
|
+
pageId = request.url.slice(idStartIndex).replace('?id=', '');
|
|
783
797
|
} else {
|
|
784
798
|
pageId = 'unknown';
|
|
785
799
|
}
|
|
786
800
|
|
|
787
|
-
// eslint-disable-next-line no-param-reassign
|
|
788
801
|
socket.nssUid = pageId;
|
|
789
802
|
|
|
790
803
|
// Overwrite the default send method to NSS's standard.
|
|
791
804
|
const originalSend = socket.send.bind(socket);
|
|
792
|
-
|
|
805
|
+
|
|
793
806
|
socket.send = (message) => {
|
|
794
807
|
originalSend(JSON.stringify({
|
|
795
808
|
message,
|
|
@@ -835,7 +848,7 @@ class NodeSimpleServer {
|
|
|
835
848
|
// Pull out specific route if there is one.
|
|
836
849
|
let route = null;
|
|
837
850
|
if ('route' in msgObj) {
|
|
838
|
-
route = msgObj
|
|
851
|
+
({ route } = msgObj);
|
|
839
852
|
}
|
|
840
853
|
|
|
841
854
|
// See if the message belongs to a callback and send it there.
|
|
@@ -927,7 +940,7 @@ class NodeSimpleServer {
|
|
|
927
940
|
* requests so they get passed on to the this.#socket.connection listener.
|
|
928
941
|
*/
|
|
929
942
|
if (request.headers.upgrade === 'websocket') {
|
|
930
|
-
|
|
943
|
+
|
|
931
944
|
return;
|
|
932
945
|
}
|
|
933
946
|
});
|
|
@@ -997,12 +1010,14 @@ class NodeSimpleServer {
|
|
|
997
1010
|
// If any back-end files are being watched for changes stop monitoring them.
|
|
998
1011
|
this.watchEnd();
|
|
999
1012
|
// Close all socket connections; these would force the server to stay up.
|
|
1000
|
-
const keys = Object.keys(this.#sockets);
|
|
1013
|
+
const keys = Object.keys(this.#sockets.routes);
|
|
1001
1014
|
keys.forEach((key) => {
|
|
1002
|
-
this.#sockets.routes[key]
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1015
|
+
if (this.#sockets.routes[key]) {
|
|
1016
|
+
this.#sockets.routes[key].forEach((socket) => {
|
|
1017
|
+
socket.send('close');
|
|
1018
|
+
socket.close();
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1006
1021
|
});
|
|
1007
1022
|
// Now gracefully close SERVER and SOCKET.
|
|
1008
1023
|
this.#server.close();
|
|
@@ -1064,7 +1079,7 @@ class NodeSimpleServer {
|
|
|
1064
1079
|
* directory to NSS's root if the setting is missing.
|
|
1065
1080
|
*/
|
|
1066
1081
|
if (!options.cwd) {
|
|
1067
|
-
|
|
1082
|
+
|
|
1068
1083
|
options.cwd = this.#OPS.root;
|
|
1069
1084
|
}
|
|
1070
1085
|
// Convert paths to array if it's not already.
|
|
@@ -1078,7 +1093,7 @@ class NodeSimpleServer {
|
|
|
1078
1093
|
this.#watching.push(watcher);
|
|
1079
1094
|
// Prepare to modify some of the standard Chokidar listeners.
|
|
1080
1095
|
const alterAddUpdates = ['add', 'addDir', 'change'];
|
|
1081
|
-
const
|
|
1096
|
+
const alterCatchAlls = ['all', 'raw'];
|
|
1082
1097
|
const alterUnlinks = ['unlink', 'unlinkDir'];
|
|
1083
1098
|
// Hookup requested listeners; they are case sensitive so type them right in your code!
|
|
1084
1099
|
Object.keys(options.events).forEach((key) => {
|
|
@@ -1088,12 +1103,12 @@ class NodeSimpleServer {
|
|
|
1088
1103
|
* Chokidar provides paths in the correct OS format but NSS will change
|
|
1089
1104
|
* all backslashes (\) into forward slashes (/).
|
|
1090
1105
|
*/
|
|
1091
|
-
if (
|
|
1106
|
+
if (alterCatchAlls.includes(key)) {
|
|
1092
1107
|
watcher.on(key, (evt, path, statsOrDetails = {}) => {
|
|
1093
1108
|
// Capture the call and alter the path before passing it on.
|
|
1094
1109
|
const altPath = path.replace(/\\/g, '/');
|
|
1095
1110
|
// Since we're messing with the path already grab the extension for the user.
|
|
1096
|
-
|
|
1111
|
+
|
|
1097
1112
|
statsOrDetails.ext = Path.extname(altPath).replace('.', '');
|
|
1098
1113
|
options.events[key](evt, altPath, statsOrDetails);
|
|
1099
1114
|
});
|
|
@@ -1102,7 +1117,7 @@ class NodeSimpleServer {
|
|
|
1102
1117
|
// Capture the call and alter the path before passing it on.
|
|
1103
1118
|
const altPath = path.replace(/\\/g, '/');
|
|
1104
1119
|
// Since we're messing with the path already grab the extension for the user.
|
|
1105
|
-
|
|
1120
|
+
|
|
1106
1121
|
statsOrDetails.ext = Path.extname(altPath).replace('.', '');
|
|
1107
1122
|
options.events[key](altPath, statsOrDetails);
|
|
1108
1123
|
});
|
|
@@ -1145,7 +1160,7 @@ class NodeSimpleServer {
|
|
|
1145
1160
|
*/
|
|
1146
1161
|
whatIs(unknown) {
|
|
1147
1162
|
try {
|
|
1148
|
-
return
|
|
1163
|
+
return {}.toString.call(unknown).match(/\s([^\]]+)/)[1].toLowerCase();
|
|
1149
1164
|
} catch (e) { return undefined; }
|
|
1150
1165
|
}
|
|
1151
1166
|
|
package/bin/print.js
CHANGED
package/changelogs/v4.md
CHANGED
|
@@ -1,3 +1,31 @@
|
|
|
1
|
+
### NSS 4.2.7 (5 January 2026)
|
|
2
|
+
|
|
3
|
+
- build: Migrate ESLint configuration to flat config format
|
|
4
|
+
- build: Add custom ESLint rules for HTML, JavaScript, and JSON
|
|
5
|
+
- deps: Update chokidar from ^3.5.3 to ^5.0.0
|
|
6
|
+
- deps: Update ws from ^8.13.0 to ^8.19.0
|
|
7
|
+
- deps: Replace eslint-config-airbnb-base with @html-eslint/eslint-plugin and eslint-plugin-jsonc
|
|
8
|
+
- deps: Update ESLint from ^8.2.0 to ^9.39.2
|
|
9
|
+
- deps: Add engines field specifying Node.js >=14.0.0 requirement
|
|
10
|
+
- feat: Enhance getAddresses() to handle optional port parameter with fallback logic
|
|
11
|
+
- feat: Enhance printListeningAddresses() to accept port parameter while maintaining backward compatibility
|
|
12
|
+
- fix: Replace deprecated substr() with slice() method
|
|
13
|
+
- fix: Use object destructuring for better code quality (prefer-destructuring rule)
|
|
14
|
+
- fix: Update arrow functions to use block statements (arrow-body-style rule)
|
|
15
|
+
- refactor: Replace manual string padding with padStart() method
|
|
16
|
+
- refactor: Remove unused eslint-disable directives
|
|
17
|
+
- style: Fix HTML meta tags formatting (remove self-closing, sort attributes)
|
|
18
|
+
- style: Remove unnecessary type attributes from script tags
|
|
19
|
+
- style: Fix SVG attribute ordering in HTML handlers
|
|
20
|
+
- docs: Update README.md with pnpm installation instructions
|
|
21
|
+
- docs: Improve installation documentation structure
|
|
22
|
+
- docs: Update watcher callback example to reflect Chokidar v4+ API changes
|
|
23
|
+
|
|
24
|
+
### NSS 4.2.5 (23 July 2024)
|
|
25
|
+
|
|
26
|
+
- chore: Document changes since 4.2.2 here in the changelog:
|
|
27
|
+
- Allow users to control address printing instead or printing it automatically.
|
|
28
|
+
|
|
1
29
|
### NSS 4.2.2 (24 January 2024)
|
|
2
30
|
|
|
3
31
|
- feat: Add WebSocket to connection with backend routes.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caboodle Tech's opinionated rules for linting HTML with ESLint. Feel free to adapt or modify
|
|
3
|
+
* these rules to suit your needs.
|
|
4
|
+
*
|
|
5
|
+
* ESLint Plugin: {@link https://www.npmjs.com/package/@html-eslint/eslint-plugin @html-eslint/eslint-plugin}
|
|
6
|
+
* HTML ESLint Parser: {@link https://html-eslint.org/docs/rules HTML ESLint Docs}
|
|
7
|
+
*/
|
|
8
|
+
export default {
|
|
9
|
+
'@html-eslint/indent': ['error', 4],
|
|
10
|
+
'@html-eslint/no-duplicate-attrs': 'error',
|
|
11
|
+
'@html-eslint/no-duplicate-id': 'error',
|
|
12
|
+
'@html-eslint/no-extra-spacing-attrs': 'error',
|
|
13
|
+
'@html-eslint/no-inline-styles': 'warn',
|
|
14
|
+
'@html-eslint/no-multiple-empty-lines': ['error', { max: 1 }],
|
|
15
|
+
'@html-eslint/no-obsolete-tags': 'error',
|
|
16
|
+
'@html-eslint/no-script-style-type': 'error',
|
|
17
|
+
'@html-eslint/no-trailing-spaces': 'error',
|
|
18
|
+
'@html-eslint/require-button-type': 'warn',
|
|
19
|
+
'@html-eslint/require-closing-tags': 'error',
|
|
20
|
+
'@html-eslint/require-doctype': 'error',
|
|
21
|
+
'@html-eslint/require-li-container': 'warn',
|
|
22
|
+
'@html-eslint/require-meta-viewport': 'error',
|
|
23
|
+
'@html-eslint/sort-attrs': 'warn'
|
|
24
|
+
};
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caboodle Tech's opinionated rules for linting JavaScript with ESLint. Feel free to adapt or
|
|
3
|
+
* modify these rules to suit your needs.
|
|
4
|
+
*
|
|
5
|
+
* ESLint Rules: {@link https://eslint.org/docs/latest/rules/ ESLint Docs}
|
|
6
|
+
*/
|
|
7
|
+
export default {
|
|
8
|
+
'class-methods-use-this': 'off',
|
|
9
|
+
'comma-dangle': ['error', 'never'],
|
|
10
|
+
'default-case': 'off',
|
|
11
|
+
indent: [
|
|
12
|
+
'error',
|
|
13
|
+
4,
|
|
14
|
+
{
|
|
15
|
+
SwitchCase: 1
|
|
16
|
+
}
|
|
17
|
+
],
|
|
18
|
+
'max-len': ['warn', 120],
|
|
19
|
+
'no-continue': 'warn',
|
|
20
|
+
'no-param-reassign': 'warn',
|
|
21
|
+
'no-plusplus': [
|
|
22
|
+
'error',
|
|
23
|
+
{
|
|
24
|
+
allowForLoopAfterthoughts: true
|
|
25
|
+
}
|
|
26
|
+
],
|
|
27
|
+
'no-restricted-syntax': 'off',
|
|
28
|
+
'no-use-before-define': 'off',
|
|
29
|
+
'no-unused-vars': ['error', { argsIgnorePattern: '^_' }],
|
|
30
|
+
'object-curly-spacing': ['error', 'always'],
|
|
31
|
+
'padded-blocks': [
|
|
32
|
+
'error',
|
|
33
|
+
{
|
|
34
|
+
classes: 'always'
|
|
35
|
+
}
|
|
36
|
+
],
|
|
37
|
+
'arrow-body-style': ['error', 'always'],
|
|
38
|
+
'arrow-parens': ['error', 'always'],
|
|
39
|
+
'arrow-spacing': ['error', { before: true, after: true }],
|
|
40
|
+
'block-spacing': ['error', 'always'],
|
|
41
|
+
'brace-style': ['error', '1tbs', { allowSingleLine: true }],
|
|
42
|
+
'comma-spacing': ['error', { before: false, after: true }],
|
|
43
|
+
'computed-property-spacing': ['error', 'never'],
|
|
44
|
+
'dot-location': ['error', 'property'],
|
|
45
|
+
'dot-notation': ['error', { allowKeywords: true }],
|
|
46
|
+
'eol-last': ['error', 'always'],
|
|
47
|
+
eqeqeq: ['error', 'always', { null: 'ignore' }],
|
|
48
|
+
'func-call-spacing': ['error', 'never'],
|
|
49
|
+
'func-name-matching': 'error',
|
|
50
|
+
'func-names': ['error', 'always'],
|
|
51
|
+
'func-style': [
|
|
52
|
+
'error',
|
|
53
|
+
'declaration',
|
|
54
|
+
{ allowArrowFunctions: true }
|
|
55
|
+
],
|
|
56
|
+
'generator-star-spacing': ['error', { before: true, after: true }],
|
|
57
|
+
'key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
|
58
|
+
'keyword-spacing': ['error', { before: true, after: true }],
|
|
59
|
+
'lines-between-class-members': [
|
|
60
|
+
'error',
|
|
61
|
+
'always',
|
|
62
|
+
{ exceptAfterSingleLine: true }
|
|
63
|
+
],
|
|
64
|
+
'newline-per-chained-call': ['error', { ignoreChainWithDepth: 4 }],
|
|
65
|
+
'no-confusing-arrow': ['error', { allowParens: true }],
|
|
66
|
+
'no-dupe-class-members': 'error',
|
|
67
|
+
'no-duplicate-imports': 'error',
|
|
68
|
+
'no-else-return': ['error', { allowElseIf: false }],
|
|
69
|
+
'no-eval': 'error',
|
|
70
|
+
'no-extra-parens': [
|
|
71
|
+
'error',
|
|
72
|
+
'all',
|
|
73
|
+
{ nestedBinaryExpressions: false }
|
|
74
|
+
],
|
|
75
|
+
'no-floating-decimal': 'error',
|
|
76
|
+
'no-lonely-if': 'error',
|
|
77
|
+
'no-mixed-operators': 'error',
|
|
78
|
+
'no-multi-assign': 'error',
|
|
79
|
+
'no-multiple-empty-lines': [
|
|
80
|
+
'error',
|
|
81
|
+
{ max: 1, maxEOF: 0, maxBOF: 0 }
|
|
82
|
+
],
|
|
83
|
+
'no-return-await': 'error',
|
|
84
|
+
'no-self-compare': 'error',
|
|
85
|
+
'no-trailing-spaces': 'error',
|
|
86
|
+
'no-unneeded-ternary': 'error',
|
|
87
|
+
'no-useless-concat': 'error',
|
|
88
|
+
'no-var': 'error',
|
|
89
|
+
'object-curly-newline': ['error', { consistent: true }],
|
|
90
|
+
'object-curly-spacing': ['error', 'always'],
|
|
91
|
+
'object-shorthand': [
|
|
92
|
+
'error',
|
|
93
|
+
'always',
|
|
94
|
+
{ ignoreConstructors: false, avoidQuotes: true }
|
|
95
|
+
],
|
|
96
|
+
'one-var': ['error', 'never'],
|
|
97
|
+
'one-var-declaration-per-line': ['error', 'always'],
|
|
98
|
+
'operator-assignment': ['error', 'always'],
|
|
99
|
+
'operator-linebreak': ['error', 'after'],
|
|
100
|
+
'prefer-arrow-callback': 'error',
|
|
101
|
+
'prefer-const': 'error',
|
|
102
|
+
'prefer-destructuring': [
|
|
103
|
+
'error',
|
|
104
|
+
{
|
|
105
|
+
VariableDeclarator: { array: false, object: true },
|
|
106
|
+
AssignmentExpression: { array: true, object: true }
|
|
107
|
+
}
|
|
108
|
+
],
|
|
109
|
+
'prefer-numeric-literals': 'error',
|
|
110
|
+
'prefer-template': 'error',
|
|
111
|
+
'quote-props': ['error', 'as-needed'],
|
|
112
|
+
quotes: [
|
|
113
|
+
'error',
|
|
114
|
+
'single',
|
|
115
|
+
{ avoidEscape: true, allowTemplateLiterals: true }
|
|
116
|
+
],
|
|
117
|
+
'require-await': 'off',
|
|
118
|
+
'rest-spread-spacing': ['error', 'never'],
|
|
119
|
+
semi: ['error', 'always'],
|
|
120
|
+
'semi-spacing': ['error', { before: false, after: true }],
|
|
121
|
+
'semi-style': ['error', 'last'],
|
|
122
|
+
'space-before-blocks': 'error',
|
|
123
|
+
'space-before-function-paren': ['error', 'never'],
|
|
124
|
+
'space-in-parens': ['error', 'never'],
|
|
125
|
+
'space-infix-ops': 'error',
|
|
126
|
+
'space-unary-ops': ['error', { words: true, nonwords: false }],
|
|
127
|
+
'spaced-comment': [
|
|
128
|
+
'error',
|
|
129
|
+
'always',
|
|
130
|
+
{
|
|
131
|
+
line: { markers: ['/'] },
|
|
132
|
+
block: {
|
|
133
|
+
balanced: true,
|
|
134
|
+
markers: ['*'],
|
|
135
|
+
exceptions: ['*']
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
],
|
|
139
|
+
'template-curly-spacing': ['error', 'never'],
|
|
140
|
+
'template-tag-spacing': ['error', 'never'],
|
|
141
|
+
yoda: ['error', 'never', { exceptRange: true }]
|
|
142
|
+
};
|
|
143
|
+
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Caboodle Tech's opinionated rules for linting JSON with ESLint. Feel free to adapt or modify
|
|
3
|
+
* these rules to suit your needs.
|
|
4
|
+
*
|
|
5
|
+
* ESLint Plugin: {@link https://www.npmjs.com/package/eslint-plugin-jsonc eslint-plugin-jsonc}
|
|
6
|
+
* JSON ESLint Parser: {@link https://www.npmjs.com/package/jsonc-eslint-parser jsonc-eslint-parser}
|
|
7
|
+
* JSON ESLint Rules: {@link https://ota-meshi.github.io/eslint-plugin-jsonc/rules/ JSON ESLint Docs}
|
|
8
|
+
*/
|
|
9
|
+
export default {
|
|
10
|
+
'jsonc/array-bracket-spacing': ['error', 'never'],
|
|
11
|
+
'jsonc/comma-dangle': ['error', 'never'],
|
|
12
|
+
'jsonc/comma-style': ['error', 'last'],
|
|
13
|
+
'jsonc/indent': ['error', 4],
|
|
14
|
+
'jsonc/key-spacing': ['error', { beforeColon: false, afterColon: true }],
|
|
15
|
+
'jsonc/object-curly-newline': ['error', { consistent: true }],
|
|
16
|
+
'jsonc/object-curly-spacing': ['error', 'always'],
|
|
17
|
+
'jsonc/object-property-newline': ['error', { allowMultiplePropertiesPerLine: true }],
|
|
18
|
+
'jsonc/quote-props': ['error', 'always'],
|
|
19
|
+
'jsonc/quotes': ['error', 'double']
|
|
20
|
+
};
|
|
21
|
+
|
package/eslint.config.js
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import HtmlParser from '@html-eslint/parser';
|
|
2
|
+
import HtmlPlugin from '@html-eslint/eslint-plugin';
|
|
3
|
+
import HtmlRules from './eslint/html-rules.js';
|
|
4
|
+
import JsRules from './eslint/js-rules.js';
|
|
5
|
+
import JsonRules from './eslint/json-rules.js';
|
|
6
|
+
import JsoncParser from 'jsonc-eslint-parser';
|
|
7
|
+
import JsoncPlugin from 'eslint-plugin-jsonc';
|
|
8
|
+
|
|
9
|
+
export default [
|
|
10
|
+
{
|
|
11
|
+
// Caboodle Tech HTML settings.
|
|
12
|
+
...HtmlPlugin.configs['flat/recommended'],
|
|
13
|
+
files: ['**/*.html'],
|
|
14
|
+
plugins: {
|
|
15
|
+
'@html-eslint': HtmlPlugin
|
|
16
|
+
},
|
|
17
|
+
rules: { ...HtmlRules },
|
|
18
|
+
languageOptions: {
|
|
19
|
+
parser: HtmlParser
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
{
|
|
23
|
+
// Caboodle Tech JavaScript settings.
|
|
24
|
+
files: ['**/*.js'],
|
|
25
|
+
languageOptions: {
|
|
26
|
+
ecmaVersion: 'latest',
|
|
27
|
+
sourceType: 'module'
|
|
28
|
+
},
|
|
29
|
+
rules: { ...JsRules }
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
// Caboodle Tech JSON settings.
|
|
33
|
+
files: ['**/*.json'],
|
|
34
|
+
languageOptions: {
|
|
35
|
+
parser: JsoncParser,
|
|
36
|
+
ecmaVersion: 'latest'
|
|
37
|
+
},
|
|
38
|
+
plugins: {
|
|
39
|
+
jsonc: JsoncPlugin
|
|
40
|
+
},
|
|
41
|
+
rules: { ...JsonRules }
|
|
42
|
+
}
|
|
43
|
+
];
|
|
44
|
+
|
|
@@ -77,6 +77,9 @@ const WebsocketDemo = () => {
|
|
|
77
77
|
// Register our websocket handler to respond to only index pages.
|
|
78
78
|
server.addWebsocketCallback('index.html', websocketHandler);
|
|
79
79
|
|
|
80
|
+
// Log the server's address so we can access it.
|
|
81
|
+
server.printListeningAddresses();
|
|
82
|
+
|
|
80
83
|
// NOTE: We could add as many callbacks as we like for different pages or patterns.
|
|
81
84
|
};
|
|
82
85
|
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<!-- This is NSS's directory listing page. -->
|
|
5
|
-
<meta charset="UTF-8"
|
|
6
|
-
<meta
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
|
7
7
|
<title>Directory Listing</title>
|
|
8
8
|
<style>
|
|
9
9
|
html,
|
|
@@ -90,7 +90,7 @@
|
|
|
90
90
|
<div class="col">
|
|
91
91
|
<h1>
|
|
92
92
|
Listing
|
|
93
|
-
<script
|
|
93
|
+
<script>
|
|
94
94
|
document.write(document.location.pathname);
|
|
95
95
|
</script>
|
|
96
96
|
</h1>
|
|
@@ -98,8 +98,8 @@
|
|
|
98
98
|
</div>
|
|
99
99
|
<div class="row content">
|
|
100
100
|
<div class="col">
|
|
101
|
-
<svg class="icon"
|
|
102
|
-
<path d="M22 24h-20v-24h14l6 6v18zm-7-23h-12v22h18v-16h-6v-6zm3 15v1h-12v-1h12zm0-3v1h-12v-1h12zm0-3v1h-12v-1h12zm-2-4h4.586l-4.586-4.586v4.586z"
|
|
101
|
+
<svg class="icon" clip-rule="evenodd" fill-rule="evenodd" height="24" width="24" xmlns="http://www.w3.org/2000/svg">
|
|
102
|
+
<path d="M22 24h-20v-24h14l6 6v18zm-7-23h-12v22h18v-16h-6v-6zm3 15v1h-12v-1h12zm0-3v1h-12v-1h12zm0-3v1h-12v-1h12zm-2-4h4.586l-4.586-4.586v4.586z"/>
|
|
103
103
|
</svg>
|
|
104
104
|
<span class="title">Files</span>
|
|
105
105
|
<ul>
|
|
@@ -107,8 +107,8 @@
|
|
|
107
107
|
</ul>
|
|
108
108
|
</div>
|
|
109
109
|
<div class="col">
|
|
110
|
-
<svg class="icon"
|
|
111
|
-
<path d="M6.083 4c1.38 1.612 2.578 3 4.917 3h11v13h-20v-16h4.083zm.917-2h-7v20h24v-17h-13c-1.629 0-2.305-1.058-4-3z"
|
|
110
|
+
<svg class="icon" height="24" viewBox="0 0 24 24" width="24" xmlns="http://www.w3.org/2000/svg">
|
|
111
|
+
<path d="M6.083 4c1.38 1.612 2.578 3 4.917 3h11v13h-20v-16h4.083zm.917-2h-7v20h24v-17h-13c-1.629 0-2.305-1.058-4-3z"/>
|
|
112
112
|
</svg>
|
|
113
113
|
<span class="title">Directories</span>
|
|
114
114
|
<ul>
|
|
@@ -121,11 +121,11 @@
|
|
|
121
121
|
<p>
|
|
122
122
|
<i>
|
|
123
123
|
Node Simple Server (NSS) {{version}} Server
|
|
124
|
-
<script
|
|
124
|
+
<script>
|
|
125
125
|
document.write(`${document.location.protocol}//${document.location.hostname}`);
|
|
126
126
|
</script>
|
|
127
127
|
Port
|
|
128
|
-
<script
|
|
128
|
+
<script>
|
|
129
129
|
document.write(document.location.port);
|
|
130
130
|
</script>
|
|
131
131
|
</i>
|
package/handlers/forbidden.html
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<!-- This is NSS's forbidden page. -->
|
|
5
|
-
<meta charset="UTF-8"
|
|
6
|
-
<meta
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
|
7
7
|
<title>403 Forbidden</title>
|
|
8
8
|
<style>
|
|
9
9
|
html,
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<h1>403 Forbidden</h1>
|
|
21
21
|
<p>
|
|
22
22
|
You don't have permission to access
|
|
23
|
-
<script
|
|
23
|
+
<script>
|
|
24
24
|
document.write(document.location.pathname);
|
|
25
25
|
</script>
|
|
26
26
|
on this server.
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
<p>
|
|
30
30
|
<i>
|
|
31
31
|
Node Simple Server (NSS) {{version}} Server
|
|
32
|
-
<script
|
|
32
|
+
<script>
|
|
33
33
|
document.write(`${document.location.protocol}//${document.location.hostname}`);
|
|
34
34
|
</script>
|
|
35
35
|
port
|
|
36
|
-
<script
|
|
36
|
+
<script>
|
|
37
37
|
document.write(document.location.port);
|
|
38
38
|
</script>
|
|
39
39
|
</i>
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
<style>
|
|
3
3
|
#NSS_WS{display:flex;justify-content:center;align-items:center;position:fixed;right:15px;bottom:15px;z-index:9000;width:80px;height:45px;border-radius:25px;background:#000}#NSS_WS svg{margin-left:5px;fill:#ffffff}#NSS_WS span{display:inline-block;margin-right:5px;font-family:monospace;font-size:22px;color:#fff}
|
|
4
4
|
</style>
|
|
5
|
-
<script
|
|
5
|
+
<script>
|
|
6
6
|
// <![CDATA[
|
|
7
7
|
if ('WebSocket' in window) {
|
|
8
8
|
NSS_WS = (() => {
|
package/handlers/not-found.html
CHANGED
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
<html>
|
|
3
3
|
<head>
|
|
4
4
|
<!-- This is NSS's 404 page. -->
|
|
5
|
-
<meta charset="UTF-8"
|
|
6
|
-
<meta
|
|
5
|
+
<meta charset="UTF-8">
|
|
6
|
+
<meta content="width=device-width, initial-scale=1.0" name="viewport">
|
|
7
7
|
<title>404 Not Found</title>
|
|
8
8
|
<style>
|
|
9
9
|
html,
|
|
@@ -20,7 +20,7 @@
|
|
|
20
20
|
<h1>404 Not Found</h1>
|
|
21
21
|
<p>
|
|
22
22
|
The requested URL
|
|
23
|
-
<script
|
|
23
|
+
<script>
|
|
24
24
|
document.write(document.location.pathname);
|
|
25
25
|
</script>
|
|
26
26
|
was not found on this server.
|
|
@@ -29,11 +29,11 @@
|
|
|
29
29
|
<p>
|
|
30
30
|
<i>
|
|
31
31
|
Node Simple Server (NSS) {{version}} Server
|
|
32
|
-
<script
|
|
32
|
+
<script>
|
|
33
33
|
document.write(`${document.location.protocol}//${document.location.hostname}`);
|
|
34
34
|
</script>
|
|
35
35
|
Port
|
|
36
|
-
<script
|
|
36
|
+
<script>
|
|
37
37
|
document.write(document.location.port);
|
|
38
38
|
</script>
|
|
39
39
|
</i>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@caboodle-tech/node-simple-server",
|
|
3
|
-
"version": "4.2.
|
|
3
|
+
"version": "4.2.7",
|
|
4
4
|
"description": "Node Simple Server (NSS): A small but effective node based server for development sites, customizable live reloading, and websocket support built-in.",
|
|
5
5
|
"main": "bin/nss.js",
|
|
6
6
|
"scripts": {
|
|
@@ -36,13 +36,18 @@
|
|
|
36
36
|
"url": "https://github.com/caboodle-tech/node-simple-server/issues"
|
|
37
37
|
},
|
|
38
38
|
"homepage": "https://github.com/caboodle-tech/node-simple-server#readme",
|
|
39
|
+
"engines": {
|
|
40
|
+
"node": ">=14.0.0"
|
|
41
|
+
},
|
|
39
42
|
"dependencies": {
|
|
40
|
-
"chokidar": "^
|
|
41
|
-
"ws": "^8.
|
|
43
|
+
"chokidar": "^5.0.0",
|
|
44
|
+
"ws": "^8.19.0"
|
|
42
45
|
},
|
|
43
46
|
"devDependencies": {
|
|
44
|
-
"eslint": "^
|
|
45
|
-
"eslint
|
|
46
|
-
"eslint
|
|
47
|
+
"@html-eslint/eslint-plugin": "^0.32.0",
|
|
48
|
+
"@html-eslint/parser": "^0.32.0",
|
|
49
|
+
"eslint": "^9.39.2",
|
|
50
|
+
"eslint-plugin-jsonc": "^2.21.0",
|
|
51
|
+
"jsonc-eslint-parser": "^2.4.2"
|
|
47
52
|
}
|
|
48
53
|
}
|
package/.eslintrc.json
DELETED
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"env": {
|
|
3
|
-
"browser": true,
|
|
4
|
-
"commonjs": true,
|
|
5
|
-
"es2021": true
|
|
6
|
-
},
|
|
7
|
-
"extends": [
|
|
8
|
-
"airbnb-base"
|
|
9
|
-
],
|
|
10
|
-
"overrides": [],
|
|
11
|
-
"parserOptions": {
|
|
12
|
-
"ecmaVersion": "latest"
|
|
13
|
-
},
|
|
14
|
-
"rules": {
|
|
15
|
-
"class-methods-use-this": "off",
|
|
16
|
-
"comma-dangle": [
|
|
17
|
-
"error",
|
|
18
|
-
"never"
|
|
19
|
-
],
|
|
20
|
-
"default-case": "off",
|
|
21
|
-
"import/extensions": ["error", "always", {"ignorePackages": true} ],
|
|
22
|
-
"import/no-extraneous-dependencies": [
|
|
23
|
-
"error",
|
|
24
|
-
{
|
|
25
|
-
"devDependencies": true
|
|
26
|
-
}
|
|
27
|
-
],
|
|
28
|
-
"indent": [
|
|
29
|
-
"error",
|
|
30
|
-
4,
|
|
31
|
-
{
|
|
32
|
-
"SwitchCase": 1
|
|
33
|
-
}
|
|
34
|
-
],
|
|
35
|
-
"max-len": ["warn", { "code": 120 }],
|
|
36
|
-
"no-plusplus": [
|
|
37
|
-
"error",
|
|
38
|
-
{
|
|
39
|
-
"allowForLoopAfterthoughts": true
|
|
40
|
-
}
|
|
41
|
-
],
|
|
42
|
-
"no-use-before-define": "off",
|
|
43
|
-
"padded-blocks": [
|
|
44
|
-
"error",
|
|
45
|
-
{
|
|
46
|
-
"classes": "always"
|
|
47
|
-
}
|
|
48
|
-
]
|
|
49
|
-
}
|
|
50
|
-
}
|