fix mount-require not reloading all require() modules (#28)

* npm audit fix
* add vscode debug launch configuration
* mount-require is only reloading the main source file, not any other required files #9
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 0000000..6c83c80
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,17 @@
+{
+    // Use IntelliSense to learn about possible attributes.
+    // Hover to view descriptions of existing attributes.
+    // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+    "version": "0.2.0",
+    "configurations": [
+        {
+            "type": "node",
+            "request": "launch",
+            "name": "wskdebug",
+            "skipFiles": [
+                "<node_internals>/**"
+            ],
+            "program": "${workspaceFolder}/wskdebug.js"
+        }
+    ]
+}
\ No newline at end of file
diff --git a/package-lock.json b/package-lock.json
index 13cae92..d3a0705 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1887,9 +1887,9 @@
             },
             "dependencies": {
                 "minimist": {
-                    "version": "1.2.0",
-                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-                    "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+                    "version": "1.2.5",
+                    "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz",
+                    "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==",
                     "dev": true
                 }
             }
diff --git a/src/kinds/nodejs/mount-require.js b/src/kinds/nodejs/mount-require.js
index 0c3d19e..44be9f8 100644
--- a/src/kinds/nodejs/mount-require.js
+++ b/src/kinds/nodejs/mount-require.js
@@ -36,10 +36,16 @@
     throw `'${mainFn}' is not a function in '${sourceFile}'. Specify the right function in wskdebug using --main.`;
 }
 
+function clearEntireRequireCache() {
+    Object.keys(require.cache).forEach(function(key) {
+        delete require.cache[key];
+    });
+}
+
 // eslint-disable-next-line no-unused-vars
 function main(args) { // lgtm [js/unused-local-variable]
-    // force reload of mounted action on every invocation
-    delete require.cache[require.resolve(path)];
+    // force reload of entire mounted action on every invocation
+    clearEntireRequireCache();
 
     // require and invoke main function
     return require(path)[mainFn](args);
diff --git a/test/nodejs.test.js b/test/nodejs.test.js
index 6be9444..1f4b419 100644
--- a/test/nodejs.test.js
+++ b/test/nodejs.test.js
@@ -31,6 +31,10 @@
 const test = require('./test');
 const assert = require('assert');
 const fse = require('fs-extra');
+const fs = require('fs');
+const os = require("os");
+const path = require("path");
+const sleep = require('util').promisify(setTimeout);
 
 describe('nodejs', function() {
     this.timeout(30000);
@@ -189,8 +193,6 @@
         test.assertAllNocksInvoked();
     });
 
-
-
     it("should mount and run local sources with a comment on the last line", async function() {
         test.mockActionAndInvocation(
             "myaction",
@@ -417,6 +419,120 @@
         test.assertAllNocksInvoked();
     });
 
+    it("should reload local plain sources on file modification", async function() {
+        this.timeout(10000);
+
+        // create copy in temp dir so we can modify it
+        const tmpDir = path.join(os.tmpdir(), fs.mkdtempSync("wskdebug-test-"));
+        fse.copySync("test/nodejs/plain-flat", tmpDir);
+        process.chdir(tmpDir);
+
+        test.mockActionDoubleInvocation(
+            "myaction",
+            // should not use this code if we specify local sources which return CORRECT
+            `const main = () => ({ msg: 'WRONG' });`,
+            {},
+            { msg: "CORRECT" },
+            async () => {
+                // change action.js to test reloading
+                console.log("simulating modifiying action.js...");
+
+                fs.writeFileSync(`action.js`,
+                    `
+                    'use strict';
+
+                    function main(params) {
+                        return { msg: "SECOND" };
+                    }
+                `);
+
+                await sleep(1);
+            },
+            { msg: "SECOND" },
+            true // binary
+        );
+
+        await wskdebug(`myaction action.js -p ${test.port}`);
+
+        test.assertAllNocksInvoked();
+    });
+
+    it("should reload local commonjs sources on file modification", async function() {
+        this.timeout(10000);
+
+        // create copy in temp dir so we can modify it
+        const tmpDir = path.join(os.tmpdir(), fs.mkdtempSync("wskdebug-test-"));
+        fse.copySync("test/nodejs/commonjs-flat", tmpDir);
+        process.chdir(tmpDir);
+
+        test.mockActionDoubleInvocation(
+            "myaction",
+            // should not use this code if we specify local sources which return CORRECT
+            `const main = () => ({ msg: 'WRONG' });`,
+            {},
+            { msg: "CORRECT/RESULT" },
+            async () => {
+                // change action.js to test reloading
+                console.log("simulating modifiying action.js...");
+
+                fs.writeFileSync(`action.js`,
+                    `
+                    'use strict';
+
+                    exports.main = function() {
+                        return { msg: "SECOND" };
+                    }
+                `);
+
+                await sleep(1);
+            },
+            { msg: "SECOND" },
+            true // binary
+        );
+
+        await wskdebug(`myaction action.js -p ${test.port}`);
+
+        test.assertAllNocksInvoked();
+    });
+
+    it("should reload local commonjs sources with a require() dependency on file modification", async function() {
+        this.timeout(10000);
+
+        // create copy in temp dir so we can modify it
+        const tmpDir = path.join(os.tmpdir(), fs.mkdtempSync("wskdebug-test-"));
+        fse.copySync("test/nodejs/commonjs-deps", tmpDir);
+        process.chdir(tmpDir);
+
+        test.mockActionDoubleInvocation(
+            "myaction",
+            // should not use this code if we specify local sources which return CORRECT
+            `const main = () => ({ msg: 'WRONG' });`,
+            {},
+            { msg: "FIRST" },
+            async () => {
+                // change dependency.js to test reloading of require() deps
+                console.log("simulating modifiying depdency.js...");
+
+                fs.writeFileSync(`dependency.js`,
+                    `
+                    'use strict';
+
+                    module.exports = {
+                        msg: "SECOND"
+                    }
+                `);
+
+                await sleep(1);
+            },
+            { msg: "SECOND" },
+            true // binary
+        );
+
+        await wskdebug(`myaction action.js -p ${test.port}`);
+
+        test.assertAllNocksInvoked();
+    });
+
     // TODO: test -l livereload connection
 
     // TODO: test agents - conditions (unit test agent code locally)
diff --git a/test/nodejs/commonjs-deps/action.js b/test/nodejs/commonjs-deps/action.js
new file mode 100644
index 0000000..38aef01
--- /dev/null
+++ b/test/nodejs/commonjs-deps/action.js
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+exports.main = function() {
+    return require('./dependency');
+}
diff --git a/test/nodejs/commonjs-deps/dependency.js b/test/nodejs/commonjs-deps/dependency.js
new file mode 100644
index 0000000..11b6e46
--- /dev/null
+++ b/test/nodejs/commonjs-deps/dependency.js
@@ -0,0 +1,22 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+'use strict';
+
+module.exports = {
+    msg: "FIRST"
+}
diff --git a/test/test.js b/test/test.js
index b0c5360..81f5b23 100644
--- a/test/test.js
+++ b/test/test.js
@@ -221,6 +221,81 @@
     expectAgentInvocation(action, params, expectedResult);
 }
 
+function mockActionDoubleInvocation(action, code, params, result1, runBetween, result2, binary=false) {
+    params = params || {};
+    const activationId = Date.now();
+    result1.$activationId = activationId;
+
+    mockAction(action, code, binary);
+    expectAgent(action, code, binary);
+
+    // 1st activation
+    nockActivation(action, body => body.$waitForActivation === true)
+        .reply(200, {
+            response: {
+                result: Object.assign(params, { $activationId: activationId })
+            }
+        });
+
+    // wskdebug sending result back to agent
+    nockActivation(
+        action,
+        body => {
+            assert.deepStrictEqual(body, result1);
+            return true;
+        }
+    ).reply(200, {
+        response: {
+            result: {
+                message: "Completed"
+            }
+        }
+    });
+
+    // 2nd activation
+    const activationId2 = Date.now() + "-second";
+    result2.$activationId = activationId2;
+
+    nockActivation(action, body => body.$waitForActivation === true)
+        .reply(200, () => {
+            runBetween();
+            return {
+                response: {
+                    result: Object.assign(params, { $activationId: activationId2 })
+                }
+            }
+        });
+
+    // wskdebug sending 2nd result back to agent
+    nockActivation(
+        action,
+        body => {
+            assert.deepStrictEqual(body, result2);
+            return true;
+        }
+    ).reply(200, {
+        response: {
+            result: {
+                message: "Completed"
+            }
+        }
+    });
+
+    // graceful shutdown for wskdebug to end test
+    nockActivation(action, body => body.$waitForActivation === true)
+        .reply(502, {
+            response: {
+                success: false,
+                result: {
+                    error: {
+                        error: "Please exit, thanks.",
+                        code: 43 // graceful exit
+                    }
+                }
+            }
+        });
+}
+
 // --------------------------------------------< internal >---------------
 
 function nodejsActionDescription(name, binary=false) {
@@ -410,6 +485,7 @@
     assertAllNocksInvoked,
     // mock
     mockActionAndInvocation,
+    mockActionDoubleInvocation,
     // advanced
     mockAction,
     expectAgent,