@caboodle-tech/node-simple-server 4.2.4 → 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 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
- You can install and use NSS locally in a project with:
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
- # As a normal dependency:
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
- # or as a development dependency:
36
- npm install @caboodle-tech/node-simple-server --save-dev
33
+ ```javascript
34
+ import NodeSimpleServer from '@caboodle-tech/node-simple-server';
37
35
  ```
38
36
 
39
- Depending on how you use and incorporate NSS into your project will determine the best dependency strategy to use.
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
- npm install --global @caboodle-tech/node-simple-server
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, extension) {
87
+ function watcherCallback(event, path, statsOrDetails) {
88
+ const extension = statsOrDetails.ext;
82
89
  if (extension === 'css') {
83
90
  Server.reloadAllStyles();
84
91
  return;
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
- // eslint-disable-next-line no-underscore-dangle
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.4';
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:${port}`,
134
- `http://127.0.0.1:${port}`
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]}:${port}`);
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
- if (dayNum.length < 10) {
226
- dayNum = `0${dayNum}`;
227
- }
228
- if (hour.length < 10) {
229
- hour = `0${hour}`;
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).mtime;
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, no-template-curly-in-string
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
  *
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.
380
381
  * @param {boolean} [returnInstead=false] If true the function will return the message string instead.
381
382
  */
382
- // eslint-disable-next-line consistent-return
383
- printListeningAddresses(returnInstead = false) {
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
+
384
397
  let message = 'Node Simple Server live @:\n';
385
- const addresses = this.getAddresses(this.#OPS.port);
398
+ const addresses = this.getAddresses(port);
386
399
  addresses.forEach((address) => {
387
400
  message += ` ${address}\n`;
388
401
  });
389
402
  message += '\n';
390
403
 
391
- if (returnInstead) {
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.substr(1, pattern.length - 2);
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
- let cleanURL = request.url.substr(1, request.url.indexOf('/ws?id=') - 1);
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.substr(idStartIndex).replace('?id=', '');
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
- // eslint-disable-next-line no-param-reassign
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.route;
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
- // eslint-disable-next-line no-useless-return
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].forEach((socket) => {
1003
- socket.send('close');
1004
- socket.close();
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
- // eslint-disable-next-line no-param-reassign
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 alterCatachAlls = ['all', 'raw'];
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 (alterCatachAlls.includes(key)) {
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
- // eslint-disable-next-line no-param-reassign
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
- // eslint-disable-next-line no-param-reassign
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 ({}).toString.call(unknown).match(/\s([^\]]+)/)[1].toLowerCase();
1163
+ return {}.toString.call(unknown).match(/\s([^\]]+)/)[1].toLowerCase();
1149
1164
  } catch (e) { return undefined; }
1150
1165
  }
1151
1166
 
package/bin/print.js CHANGED
@@ -1,5 +1,3 @@
1
- /* eslint-disable no-console */
2
-
3
1
  class Print {
4
2
 
5
3
  #enabled = true;
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
+
@@ -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
+
@@ -57,6 +57,9 @@ const WebsiteDemo = () => {
57
57
  * setting up `watch`.
58
58
  */
59
59
  server.watch(websiteRoot, watcherOptions);
60
+
61
+ // Log the server's address so we can access it.
62
+ server.printListeningAddresses();
60
63
  };
61
64
 
62
65
  export default WebsiteDemo;
@@ -55,6 +55,9 @@ const WebsiteDemo = () => {
55
55
  * setting up `watch`.
56
56
  */
57
57
  server.watch(websiteRoot, watcherOptions);
58
+
59
+ // Log the server's address so we can access it.
60
+ server.printListeningAddresses();
58
61
  };
59
62
 
60
63
  export default WebsiteDemo;
@@ -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 name="viewport" content="width=device-width, initial-scale=1.0" />
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 type="text/javascript">
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" width="24" height="24" xmlns="http://www.w3.org/2000/svg" fill-rule="evenodd" clip-rule="evenodd">
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" xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24">
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 type="text/javascript">
124
+ <script>
125
125
  document.write(`${document.location.protocol}//${document.location.hostname}`);
126
126
  </script>
127
127
  Port
128
- <script type="text/javascript">
128
+ <script>
129
129
  document.write(document.location.port);
130
130
  </script>
131
131
  </i>
@@ -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 name="viewport" content="width=device-width, initial-scale=1.0" />
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 type="text/javascript">
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 type="text/javascript">
32
+ <script>
33
33
  document.write(`${document.location.protocol}//${document.location.hostname}`);
34
34
  </script>
35
35
  port
36
- <script type="text/javascript">
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 type='text/javascript'>
5
+ <script>
6
6
  // <![CDATA[
7
7
  if ('WebSocket' in window) {
8
8
  NSS_WS = (() => {
@@ -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 name="viewport" content="width=device-width, initial-scale=1.0" />
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 type="text/javascript">
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 type="text/javascript">
32
+ <script>
33
33
  document.write(`${document.location.protocol}//${document.location.hostname}`);
34
34
  </script>
35
35
  Port
36
- <script type="text/javascript">
36
+ <script>
37
37
  document.write(document.location.port);
38
38
  </script>
39
39
  </i>
@@ -1,5 +1,5 @@
1
1
  <!-- Code injected by Node Simple Server. -->
2
- <script type="text/javascript">
2
+ <script>
3
3
  // <![CDATA[
4
4
  if ("WebSocket" in window) {
5
5
  NSS_WS = (() => {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@caboodle-tech/node-simple-server",
3
- "version": "4.2.4",
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": "^3.5.3",
41
- "ws": "^8.13.0"
43
+ "chokidar": "^5.0.0",
44
+ "ws": "^8.19.0"
42
45
  },
43
46
  "devDependencies": {
44
- "eslint": "^8.2.0",
45
- "eslint-config-airbnb-base": "^15.0.0",
46
- "eslint-plugin-import": "^2.25.2"
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
- }