From ff1c9a0f2b110a9736a63317c1f3563ee33c8721 Mon Sep 17 00:00:00 2001
From: Ram <ramdamera@outlook.com>
Date: Tue, 27 Mar 2018 19:21:07 +0530
Subject: [PATCH] Refactor code

* Add jsdoc

* Add type checking for options in API

* Minor tweaks

* Update yargs

* Bump version to 0.2.0
---
 .gitignore                    |  1 +
 bin/serv.js                   |  2 +-
 lib/helpers/https-redirect.js |  1 -
 lib/helpers/logger.js         |  2 +-
 lib/index.js                  | 35 +++++++++++++++++++++++++++--------
 lib/static-serve.js           | 30 +++++++++++++++++++++++++++---
 package.json                  |  4 ++--
 7 files changed, 59 insertions(+), 16 deletions(-)

diff --git a/.gitignore b/.gitignore
index eb03e3e..740a0a4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,2 +1,3 @@
 node_modules
 *.log
+package-lock.json
diff --git a/bin/serv.js b/bin/serv.js
index baded62..33c37c1 100644
--- a/bin/serv.js
+++ b/bin/serv.js
@@ -19,7 +19,7 @@ const options = yargs
     's': 'Use https - self signed keys',
     'h2': 'Enable http2 protocol',
     'l': 'Enable directory listing',
-    'f': 'Enable fast mode(no compression, ETags, logging)',
+    'f': 'Enable fast mode(no compression/ETags/logging)',
   })
   .alias({
     'p': 'port',
diff --git a/lib/helpers/https-redirect.js b/lib/helpers/https-redirect.js
index 8616077..b9b3d1b 100644
--- a/lib/helpers/https-redirect.js
+++ b/lib/helpers/https-redirect.js
@@ -1,6 +1,5 @@
 'use strict';
 
-
 const HTTPSRedirect = (req, res, next) => {
   if (req.hostname === 'localhost') {
     return next();
diff --git a/lib/helpers/logger.js b/lib/helpers/logger.js
index a92c29d..74d61fd 100644
--- a/lib/helpers/logger.js
+++ b/lib/helpers/logger.js
@@ -47,7 +47,7 @@ const logger = (req, res, next) => {
   };
 
   // for outgoing response
-  const logHandler = (error, ee, event, args) => {
+  const logHandler = (error, ee, event) => {
     cleanup();
 
     if (event === 'end' || event === 'finish') {
diff --git a/lib/index.js b/lib/index.js
index dbd7082..13eeb88 100644
--- a/lib/index.js
+++ b/lib/index.js
@@ -32,11 +32,20 @@ const certificateOptions = {
  */
 class Serv {
   constructor(options) {
-    if (!options) {
-      options = Serv.DEFAULT_OPTIONS;
+    this.opts = Object.assign({}, Serv.DEFAULTS, options || {});
+
+    if (typeof this.opts.dir !== 'string') {
+      throw new Error('option.dir is not a string');
+    }
+
+    if (typeof this.opts.port !== 'number') {
+      throw new Error('option.port is not a number');
     }
 
-    this.opts = Object.assign({}, options);
+    this.opts.compress = Boolean(this.opts.compress);
+    this.opts.secure = Boolean(this.opts.secure);
+    this.opts.http2 = Boolean(this.opts.http2);
+    this.opts.listing = Boolean(this.opts.listening);
 
     try {
       this.opts.port = Number.parseInt(this.opts.port, 10);
@@ -52,7 +61,10 @@ class Serv {
    * @memberof Serv
    */
   async start() {
+    // get an available port
     const PORT = await getPort(this.opts.port);
+
+    // listen on 0.0.0.0, allows external requests
     const HOST = '::';
 
     this.opts.port = PORT;
@@ -95,7 +107,6 @@ class Serv {
 
     // A few settings for polka
     // disable etags, compression and logging in fast mode
-
     if (!options.fast) {
       // Enable SHA256 etags
       polkaApp.use(addETag);
@@ -120,12 +131,16 @@ class Serv {
       polkaApp.use(options.logger);
     }
 
-    const staticHandler = staticServe(staticAssetPath, {
-      dotfiles: 'allow',
+    // can override dotfiles, cannot override etags
+    const handlerOpts = Object.assign({
+      dotfile: 'allow',
+    }, this.opts, {
       etag: false,
       listing: options.listing,
     });
 
+    const staticHandler = staticServe(staticAssetPath, handlerOpts);
+
     polkaApp.use(staticHandler);
 
     return polkaApp;
@@ -182,6 +197,9 @@ class Serv {
     return polkaApp;
   }
 
+  /**
+   * Stop this serv instance
+   */
   async stop() {
     if (this.server && this.server.listening) {
       this.server.close();
@@ -194,7 +212,7 @@ class Serv {
    * @readonly
    * @memberof Serv
    */
-  get DEFAULT_OPTIONS() {
+  get DEFAULTS() {
     return {
       dir: '.',
       port: 8080,
@@ -212,7 +230,8 @@ class Serv {
    * @memberof Serv
    */
   get options() {
-    return this.opts;
+    // return a copy only
+    return Object.assign({}, this.opts);
   }
 }
 
diff --git a/lib/static-serve.js b/lib/static-serve.js
index 08189cd..4197987 100644
--- a/lib/static-serve.js
+++ b/lib/static-serve.js
@@ -12,6 +12,9 @@ const parseUrl = require('parseurl');
 
 /**
  * send a reponse with additional headers
+ *
+ * @param {Object} res http.ServerResponse
+ * @param {string} body data to send
  */
 const sendResponse = (res, body) => {
   res.setHeader('X-Content-Type-Options', 'nosniff');
@@ -41,6 +44,9 @@ const send505 = (res) => {
 /**
  * returns HTML containing listing of a directory
  *
+ * @param {string} root path to the root of directory
+ * @param {string} pathname relative path to root
+ *
  * @returns {string}
  */
 const getListing = async (root, pathname) => {
@@ -119,6 +125,12 @@ const getListing = async (root, pathname) => {
   return body;
 };
 
+/**
+ * return a function to execute when send hits a directory
+ *
+ * @param {Object} opts serv options
+ * @param {Object} req http.ServerRequest
+ */
 const onSendDirectory = (opts, req) => {
   return async function onDirectory(res) {
     if (opts.listing) {
@@ -129,8 +141,14 @@ const onSendDirectory = (opts, req) => {
   };
 };
 
+/**
+ * return a function to execute when send fails
+ *
+ * @param {Object} res http.ServerResponse
+ */
 const onSendError = (res) => {
   return async function onError(e) {
+    // send 404 for known errors
     if (e.code === 'ENOENT' || e.code === 'ENAMETOOLONG' || e.code === 'ENOTDIR') {
       return send404(res);
     }
@@ -139,18 +157,24 @@ const onSendError = (res) => {
   };
 };
 
-const serveStatic = (root, options) => {
-  let opts = Object.assign({}, options || {});
+/**
+ * return a http handler
+ *
+ * @param {string} root path to serve
+ * @param {Object} options serv options
+ */
+const serveStatic = (root, options = {}) => {
+  let opts = Object.assign({}, options);
 
   // setup options for send
   opts.maxage = opts.maxage || opts.maxAge || 0;
   opts.root = path.resolve(root);
   opts.index = ['index.html', 'index.htm'];
-  opts.dotfiles = 'allow';
 
   return async function(req, res) {
     res.req = req;
 
+    // no no for other type of requests
     if (req.method !== 'GET' && req.method !== 'HEAD') {
       res.statusCode = 405;
       res.setHeader('Allow', 'GET, HEAD');
diff --git a/package.json b/package.json
index fde8eb4..f9e5a18 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
 {
   "name": "@ramlmn/serv",
-  "version": "0.1.2",
+  "version": "0.2.0",
   "description": "Simple development server for static files",
   "license": "MIT",
   "repository": "ramlmn/serv",
@@ -23,7 +23,7 @@
     "polka": "^0.3.3",
     "send": "^0.16.2",
     "uuid": "^3.2.1",
-    "yargs": "^11.0.0"
+    "yargs": "^11.1.0"
   },
   "yargs": {
     "short-option-groups": false