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 AllInvokeMethods_ReceiveAndLogStderrOutput_D /// /// Specify for access to all logging output. /// - private HttpNodeJSService CreateHttpNodeJSService(StringBuilder loggerStringBuilder = null) + private HttpNodeJSService CreateHttpNodeJSService(StringBuilder loggerStringBuilder = null, + string projectPath = null) { var services = new ServiceCollection(); services.AddNodeJS(); // Default INodeService is HttpNodeService + if (projectPath != null) + { + services.Configure(options => options.ProjectPath = projectPath); + } services.AddLogging(lb => { lb. diff --git a/test/NodeJS/dummyModule.js b/test/NodeJS/Javascript/dummyModule.js similarity index 100% rename from test/NodeJS/dummyModule.js rename to test/NodeJS/Javascript/dummyModule.js diff --git a/test/NodeJS/Javascript/dummyReturnsValueModule.js b/test/NodeJS/Javascript/dummyReturnsValueModule.js new file mode 100644 index 0000000..3fa1b92 --- /dev/null +++ b/test/NodeJS/Javascript/dummyReturnsValueModule.js @@ -0,0 +1,2 @@ +// Used by HttpNodeJSServiceIntegrationTests +module.exports = 10; diff --git a/test/NodeJS/Javascript/package.json b/test/NodeJS/Javascript/package.json new file mode 100644 index 0000000..b6ed8ab --- /dev/null +++ b/test/NodeJS/Javascript/package.json @@ -0,0 +1,9 @@ +{ + "private": true, + "scripts": { + "build": "echo ------ Yarn Install ------ && yarn install" + }, + "dependencies": { + "prismjs": "^1.17.1" + } +} diff --git a/test/NodeJS/Javascript/yarn.lock b/test/NodeJS/Javascript/yarn.lock new file mode 100644 index 0000000..6616255 --- /dev/null +++ b/test/NodeJS/Javascript/yarn.lock @@ -0,0 +1,41 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +clipboard@^2.0.0: + version "2.0.4" + resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d" + integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ== + dependencies: + good-listener "^1.2.2" + select "^1.1.2" + tiny-emitter "^2.0.0" + +delegate@^3.1.2: + version "3.2.0" + resolved "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166" + integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw== + +good-listener@^1.2.2: + version "1.2.2" + resolved "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50" + integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA= + dependencies: + delegate "^3.1.2" + +prismjs@^1.17.1: + version "1.17.1" + resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz#e669fcbd4cdd873c35102881c33b14d0d68519be" + integrity sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q== + optionalDependencies: + clipboard "^2.0.0" + +select@^1.1.2: + version "1.1.2" + resolved "https://registry.npmjs.org/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d" + integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0= + +tiny-emitter@^2.0.0: + version "2.1.0" + resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423" + integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q== diff --git a/test/NodeJS/Jering.Javascript.NodeJS.Tests.csproj b/test/NodeJS/Jering.Javascript.NodeJS.Tests.csproj index 82a37f1..ed806b3 100644 --- a/test/NodeJS/Jering.Javascript.NodeJS.Tests.csproj +++ b/test/NodeJS/Jering.Javascript.NodeJS.Tests.csproj @@ -5,43 +5,46 @@ netcoreapp2.1 false true - - - ../../Jering.Javascript.NodeJS.ruleset + Javascript\node_modules\**;$(DefaultItemExcludes) - all - runtime; build; native; contentfiles; analyzers + all + runtime; build; native; contentfiles; analyzers - all - runtime; build; native; contentfiles; analyzers + all + runtime; build; native; contentfiles; analyzers - all - runtime; build; native; contentfiles; analyzers + all + runtime; build; native; contentfiles; analyzers - all - runtime; build; native; contentfiles; analyzers + all + runtime; build; native; contentfiles; analyzers + - - + - + - + + + + + + diff --git a/test/NodeJS/StaticNodeJSServiceIntegrationTests.cs b/test/NodeJS/StaticNodeJSServiceIntegrationTests.cs index 21ca476..67071af 100644 --- a/test/NodeJS/StaticNodeJSServiceIntegrationTests.cs +++ b/test/NodeJS/StaticNodeJSServiceIntegrationTests.cs @@ -139,6 +139,8 @@ public async void InvokeFromFileAsync_InvokesJavascript() { // Arrange const string dummyResultString = "success"; + StaticNodeJSService. + Configure(options => options.ProjectPath = "./Javascript"); // Act DummyResult result = await StaticNodeJSService.