diff --git a/Changelog.md b/Changelog.md
index 7d3c992..2f2fe92 100644
--- a/Changelog.md
+++ b/Changelog.md
@@ -3,7 +3,11 @@ This project uses [semantic versioning](http://semver.org/spec/v2.0.0.html). Ref
*[Semantic Versioning in Practice](https://www.jering.tech/articles/semantic-versioning-in-practice)*
for an overview of semantic versioning.
-## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/5.1.0...HEAD)
+## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/5.1.1...HEAD)
+
+## [5.1.1](https://github.com/JeringTech/Javascript.NodeJS/compare/5.1.0...5.1.1) - Nov 29, 2019
+### Fixes
+- Fixed requiring of modules from modules in string/stream form. ([#59](https://github.com/JeringTech/Javascript.NodeJS/pull/59))
## [5.1.0](https://github.com/JeringTech/Javascript.NodeJS/compare/5.0.0...5.1.0) - Nov 28, 2019
### Changes
diff --git a/perf/NodeJS/Jering.Javascript.NodeJS.Performance.csproj b/perf/NodeJS/Jering.Javascript.NodeJS.Performance.csproj
index 953e485..582c317 100644
--- a/perf/NodeJS/Jering.Javascript.NodeJS.Performance.csproj
+++ b/perf/NodeJS/Jering.Javascript.NodeJS.Performance.csproj
@@ -24,14 +24,7 @@
-
-
-
-
-
-
-
-
+
diff --git a/src/NodeJS/Javascript/Servers/OutOfProcess/Http/HttpServer.ts b/src/NodeJS/Javascript/Servers/OutOfProcess/Http/HttpServer.ts
index e3b985d..76444cd 100644
--- a/src/NodeJS/Javascript/Servers/OutOfProcess/Http/HttpServer.ts
+++ b/src/NodeJS/Javascript/Servers/OutOfProcess/Http/HttpServer.ts
@@ -66,7 +66,13 @@ const server = http.createServer((req, res) => {
// Not cached
if (exports == null) {
- let module = new Module('', null);
+ // global is analagous to window in browsers (the global namespace) - https://nodejs.org/api/globals.html#globals_global
+ // module is a reference to the current module - https://nodejs.org/api/modules.html#modules_module
+ let parent = global.module;
+ let module = new Module('', parent);
+ // This is typically done by Module._resolveLookupPaths which is called by Module._resolveFilename which in turn is called by Module._load.
+ // Since we're loading a module in string form - not an actual file with a filename - we can't use Module._load. So we do this manually.
+ module.paths = parent.paths;
module._compile(invocationRequest.moduleSource, 'anonymous');
if (invocationRequest.newCacheIdentifier != null) {
diff --git a/src/NodeJS/Javascript/Typings/extensions.d.ts b/src/NodeJS/Javascript/Typings/extensions.d.ts
index e9a1b9c..d18aa7b 100644
--- a/src/NodeJS/Javascript/Typings/extensions.d.ts
+++ b/src/NodeJS/Javascript/Typings/extensions.d.ts
@@ -1 +1,7 @@
declare function __non_webpack_require__(path: string): any;
+
+declare module NodeJS {
+ interface Global {
+ module: Module;
+ }
+}
\ No newline at end of file
diff --git a/test/NodeJS/HttpNodeJSServiceIntegrationTests.cs b/test/NodeJS/HttpNodeJSServiceIntegrationTests.cs
index 6c11ef8..ee75a9e 100644
--- a/test/NodeJS/HttpNodeJSServiceIntegrationTests.cs
+++ b/test/NodeJS/HttpNodeJSServiceIntegrationTests.cs
@@ -129,6 +129,74 @@ public async void InvokeFromStreamAsync_InvokesJavascript()
Assert.Equal(dummyResultString, result.Result);
}
+ [Fact(Timeout = _timeoutMS)]
+ public async void InvokeFromStreamAsync_LoadsRequiredModulesFromNodeModulesInProjectDirectory()
+ {
+ // Arrange
+ const string dummyCode = @"public string ExampleFunction(string arg)
+{
+ // Example comment
+ return arg + ""dummyString"";
+}";
+ HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is /bin/debug/
+
+ // Act
+ string result;
+ using (var memoryStream = new MemoryStream())
+ using (var streamWriter = new StreamWriter(memoryStream))
+ {
+ streamWriter.Write(@"const prismjs = require('prismjs');
+require('prismjs/components/prism-csharp');
+
+module.exports = (callback, code) => {
+ var result = prismjs.highlight(code, prismjs.languages.csharp, 'csharp');
+
+ callback(null, result);
+};");
+ streamWriter.Flush();
+ memoryStream.Position = 0;
+
+ // Act
+ result = await testSubject.InvokeFromStreamAsync(memoryStream, args: new[] { dummyCode }).ConfigureAwait(false);
+ }
+
+ // Assert
+ const string expectedResult = @"public string ExampleFunction(string arg)
+{
+ // Example comment
+ return arg + ""dummyString"";
+}";
+ Assert.Equal(expectedResult, result);
+ }
+
+ [Fact(Timeout = _timeoutMS)]
+ public async void InvokeFromStreamAsync_LoadsRequiredModuleFromFileInProjectDirectory()
+ {
+ // Arrange
+ HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is /bin/debug/
+
+ // Act
+ int result;
+ using (var memoryStream = new MemoryStream())
+ using (var streamWriter = new StreamWriter(memoryStream))
+ {
+ streamWriter.Write(@"const value = require('./dummyReturnsValueModule.js');
+
+module.exports = (callback) => {
+
+ callback(null, value);
+};");
+ streamWriter.Flush();
+ memoryStream.Position = 0;
+
+ // Act
+ result = await testSubject.InvokeFromStreamAsync(memoryStream).ConfigureAwait(false);
+ }
+
+ // Assert
+ Assert.Equal(10, result); // dummyReturnsValueModule.js just exports 10
+ }
+
[Fact(Timeout = _timeoutMS)]
public void InvokeFromStreamAsync_IsThreadSafe()
{
@@ -185,6 +253,54 @@ public async void InvokeFromStringAsync_InvokesJavascript()
Assert.Equal(dummyResultString, result.Result);
}
+ [Fact(Timeout = _timeoutMS)]
+ public async void InvokeFromStringAsync_LoadsRequiredModulesFromNodeModulesInProjectDirectory()
+ {
+ // Arrange
+ const string dummyCode = @"public string ExampleFunction(string arg)
+{
+ // Example comment
+ return arg + ""dummyString"";
+}";
+ HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is /bin/debug/
+
+ // Act
+ string result = await testSubject.InvokeFromStringAsync(@"const prismjs = require('prismjs');
+require('prismjs/components/prism-csharp');
+
+module.exports = (callback, code) => {
+ var result = prismjs.highlight(code, prismjs.languages.csharp, 'csharp');
+
+ callback(null, result);
+};", args: new[] { dummyCode }).ConfigureAwait(false);
+
+ // Assert
+ const string expectedResult = @"public string ExampleFunction(string arg)
+{
+ // Example comment
+ return arg + ""dummyString"";
+}";
+ Assert.Equal(expectedResult, result);
+ }
+
+ [Fact(Timeout = _timeoutMS)]
+ public async void InvokeFromStringAsync_LoadsRequiredModuleFromFileInProjectDirectory()
+ {
+ // Arrange
+ HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is /bin/debug/
+
+ // Act
+ int result = await testSubject.InvokeFromStringAsync(@"const value = require('./dummyReturnsValueModule.js');
+
+module.exports = (callback) => {
+
+ callback(null, value);
+};").ConfigureAwait(false);
+
+ // Assert
+ Assert.Equal(10, result); // dummyReturnsValueModule.js just exports 10
+ }
+
[Fact(Timeout = _timeoutMS)]
public void InvokeFromStringAsync_IsThreadSafe()
{
@@ -220,7 +336,7 @@ public void InvokeFromStringAsync_IsThreadSafe()
public async void InvokeFromFileAsync_InvokesJavascript()
{
const string dummyResultString = "success";
- HttpNodeJSService testSubject = CreateHttpNodeJSService();
+ HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/Javascript");
// Act
DummyResult result = await testSubject.
@@ -236,7 +352,7 @@ public void InvokeFromFileAsync_IsThreadSafe()
// Arrange
const string dummyModule = "dummyModule.js";
const string dummyResultString = "success";
- HttpNodeJSService testSubject = CreateHttpNodeJSService();
+ HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/Javascript");
// Act
var results = new ConcurrentQueue();
@@ -489,10 +605,15 @@ public static IEnumerable