Skip to content

Commit b2666f1

Browse files
authored
Merge pull request #59 from JeringTech/fix-loading-modules-from-string-source
Fixed loading of modules from modules in string/stream form.
2 parents 446af1c + aa4783d commit b2666f1

11 files changed

+215
-28
lines changed

Changelog.md

+5-1
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,11 @@ This project uses [semantic versioning](http://semver.org/spec/v2.0.0.html). Ref
33
*[Semantic Versioning in Practice](https://www.jering.tech/articles/semantic-versioning-in-practice)*
44
for an overview of semantic versioning.
55

6-
## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/5.1.0...HEAD)
6+
## [Unreleased](https://github.com/JeringTech/Javascript.NodeJS/compare/5.1.1...HEAD)
7+
8+
## [5.1.1](https://github.com/JeringTech/Javascript.NodeJS/compare/5.1.0...5.1.1) - Nov 29, 2019
9+
### Fixes
10+
- Fixed requiring of modules from modules in string/stream form. ([#59](https://github.com/JeringTech/Javascript.NodeJS/pull/59))
711

812
## [5.1.0](https://github.com/JeringTech/Javascript.NodeJS/compare/5.0.0...5.1.0) - Nov 28, 2019
913
### Changes

perf/NodeJS/Jering.Javascript.NodeJS.Performance.csproj

+1-8
Original file line numberDiff line numberDiff line change
@@ -24,14 +24,7 @@
2424
<ProjectReference Include="..\..\src\NodeJS\Jering.Javascript.NodeJS.csproj" />
2525
</ItemGroup>
2626

27-
<ItemGroup>
28-
<JavascriptInputs Include="package.json" />
29-
<JavascriptOutputs Include="yarn.lock" />
30-
<!-- If any file in JavascriptInputs has changed, fast up-to-date check must indicate that a rebuild is required - https://github.com/dotnet/project-system/pull/2241 -->
31-
<UpToDateCheckInput Include="@(JavascriptInputs)" />
32-
</ItemGroup>
33-
34-
<Target Name="YarnInstall" BeforeTargets="DispatchToInnerBuilds;PreBuildEvent" Inputs="@(JavascriptInputs)" Outputs="@(JavascriptOutputs)">
27+
<Target Name="YarnInstall" BeforeTargets="DispatchToInnerBuilds;PreBuildEvent">
3528
<Yarn WorkingDirectory="." Command="run build" />
3629
</Target>
3730

src/NodeJS/Javascript/Servers/OutOfProcess/Http/HttpServer.ts

+7-1
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,13 @@ const server = http.createServer((req, res) => {
6666

6767
// Not cached
6868
if (exports == null) {
69-
let module = new Module('', null);
69+
// global is analagous to window in browsers (the global namespace) - https://nodejs.org/api/globals.html#globals_global
70+
// module is a reference to the current module - https://nodejs.org/api/modules.html#modules_module
71+
let parent = global.module;
72+
let module = new Module('', parent);
73+
// This is typically done by Module._resolveLookupPaths which is called by Module._resolveFilename which in turn is called by Module._load.
74+
// 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.
75+
module.paths = parent.paths;
7076
module._compile(invocationRequest.moduleSource, 'anonymous');
7177

7278
if (invocationRequest.newCacheIdentifier != null) {
Original file line numberDiff line numberDiff line change
@@ -1 +1,7 @@
11
declare function __non_webpack_require__(path: string): any;
2+
3+
declare module NodeJS {
4+
interface Global {
5+
module: Module;
6+
}
7+
}

test/NodeJS/HttpNodeJSServiceIntegrationTests.cs

+124-3
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,74 @@ public async void InvokeFromStreamAsync_InvokesJavascript()
129129
Assert.Equal(dummyResultString, result.Result);
130130
}
131131

132+
[Fact(Timeout = _timeoutMS)]
133+
public async void InvokeFromStreamAsync_LoadsRequiredModulesFromNodeModulesInProjectDirectory()
134+
{
135+
// Arrange
136+
const string dummyCode = @"public string ExampleFunction(string arg)
137+
{
138+
// Example comment
139+
return arg + ""dummyString"";
140+
}";
141+
HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is <test project path>/bin/debug/<framework>
142+
143+
// Act
144+
string result;
145+
using (var memoryStream = new MemoryStream())
146+
using (var streamWriter = new StreamWriter(memoryStream))
147+
{
148+
streamWriter.Write(@"const prismjs = require('prismjs');
149+
require('prismjs/components/prism-csharp');
150+
151+
module.exports = (callback, code) => {
152+
var result = prismjs.highlight(code, prismjs.languages.csharp, 'csharp');
153+
154+
callback(null, result);
155+
};");
156+
streamWriter.Flush();
157+
memoryStream.Position = 0;
158+
159+
// Act
160+
result = await testSubject.InvokeFromStreamAsync<string>(memoryStream, args: new[] { dummyCode }).ConfigureAwait(false);
161+
}
162+
163+
// Assert
164+
const string expectedResult = @"<span class=""token keyword"">public</span> <span class=""token keyword"">string</span> <span class=""token function"">ExampleFunction</span><span class=""token punctuation"">(</span><span class=""token keyword"">string</span> arg<span class=""token punctuation"">)</span>
165+
<span class=""token punctuation"">{</span>
166+
<span class=""token comment"">// Example comment</span>
167+
<span class=""token keyword"">return</span> arg <span class=""token operator"">+</span> <span class=""token string"">""dummyString""</span><span class=""token punctuation"">;</span>
168+
<span class=""token punctuation"">}</span>";
169+
Assert.Equal(expectedResult, result);
170+
}
171+
172+
[Fact(Timeout = _timeoutMS)]
173+
public async void InvokeFromStreamAsync_LoadsRequiredModuleFromFileInProjectDirectory()
174+
{
175+
// Arrange
176+
HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is <test project path>/bin/debug/<framework>
177+
178+
// Act
179+
int result;
180+
using (var memoryStream = new MemoryStream())
181+
using (var streamWriter = new StreamWriter(memoryStream))
182+
{
183+
streamWriter.Write(@"const value = require('./dummyReturnsValueModule.js');
184+
185+
module.exports = (callback) => {
186+
187+
callback(null, value);
188+
};");
189+
streamWriter.Flush();
190+
memoryStream.Position = 0;
191+
192+
// Act
193+
result = await testSubject.InvokeFromStreamAsync<int>(memoryStream).ConfigureAwait(false);
194+
}
195+
196+
// Assert
197+
Assert.Equal(10, result); // dummyReturnsValueModule.js just exports 10
198+
}
199+
132200
[Fact(Timeout = _timeoutMS)]
133201
public void InvokeFromStreamAsync_IsThreadSafe()
134202
{
@@ -185,6 +253,54 @@ public async void InvokeFromStringAsync_InvokesJavascript()
185253
Assert.Equal(dummyResultString, result.Result);
186254
}
187255

256+
[Fact(Timeout = _timeoutMS)]
257+
public async void InvokeFromStringAsync_LoadsRequiredModulesFromNodeModulesInProjectDirectory()
258+
{
259+
// Arrange
260+
const string dummyCode = @"public string ExampleFunction(string arg)
261+
{
262+
// Example comment
263+
return arg + ""dummyString"";
264+
}";
265+
HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is <test project path>/bin/debug/<framework>
266+
267+
// Act
268+
string result = await testSubject.InvokeFromStringAsync<string>(@"const prismjs = require('prismjs');
269+
require('prismjs/components/prism-csharp');
270+
271+
module.exports = (callback, code) => {
272+
var result = prismjs.highlight(code, prismjs.languages.csharp, 'csharp');
273+
274+
callback(null, result);
275+
};", args: new[] { dummyCode }).ConfigureAwait(false);
276+
277+
// Assert
278+
const string expectedResult = @"<span class=""token keyword"">public</span> <span class=""token keyword"">string</span> <span class=""token function"">ExampleFunction</span><span class=""token punctuation"">(</span><span class=""token keyword"">string</span> arg<span class=""token punctuation"">)</span>
279+
<span class=""token punctuation"">{</span>
280+
<span class=""token comment"">// Example comment</span>
281+
<span class=""token keyword"">return</span> arg <span class=""token operator"">+</span> <span class=""token string"">""dummyString""</span><span class=""token punctuation"">;</span>
282+
<span class=""token punctuation"">}</span>";
283+
Assert.Equal(expectedResult, result);
284+
}
285+
286+
[Fact(Timeout = _timeoutMS)]
287+
public async void InvokeFromStringAsync_LoadsRequiredModuleFromFileInProjectDirectory()
288+
{
289+
// Arrange
290+
HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is <test project path>/bin/debug/<framework>
291+
292+
// Act
293+
int result = await testSubject.InvokeFromStringAsync<int>(@"const value = require('./dummyReturnsValueModule.js');
294+
295+
module.exports = (callback) => {
296+
297+
callback(null, value);
298+
};").ConfigureAwait(false);
299+
300+
// Assert
301+
Assert.Equal(10, result); // dummyReturnsValueModule.js just exports 10
302+
}
303+
188304
[Fact(Timeout = _timeoutMS)]
189305
public void InvokeFromStringAsync_IsThreadSafe()
190306
{
@@ -220,7 +336,7 @@ public void InvokeFromStringAsync_IsThreadSafe()
220336
public async void InvokeFromFileAsync_InvokesJavascript()
221337
{
222338
const string dummyResultString = "success";
223-
HttpNodeJSService testSubject = CreateHttpNodeJSService();
339+
HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/Javascript");
224340

225341
// Act
226342
DummyResult result = await testSubject.
@@ -236,7 +352,7 @@ public void InvokeFromFileAsync_IsThreadSafe()
236352
// Arrange
237353
const string dummyModule = "dummyModule.js";
238354
const string dummyResultString = "success";
239-
HttpNodeJSService testSubject = CreateHttpNodeJSService();
355+
HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/Javascript");
240356

241357
// Act
242358
var results = new ConcurrentQueue<DummyResult>();
@@ -489,10 +605,15 @@ public static IEnumerable<object[]> AllInvokeMethods_ReceiveAndLogStderrOutput_D
489605
/// <summary>
490606
/// Specify <paramref name="loggerStringBuilder"/> for access to all logging output.
491607
/// </summary>
492-
private HttpNodeJSService CreateHttpNodeJSService(StringBuilder loggerStringBuilder = null)
608+
private HttpNodeJSService CreateHttpNodeJSService(StringBuilder loggerStringBuilder = null,
609+
string projectPath = null)
493610
{
494611
var services = new ServiceCollection();
495612
services.AddNodeJS(); // Default INodeService is HttpNodeService
613+
if (projectPath != null)
614+
{
615+
services.Configure<NodeJSProcessOptions>(options => options.ProjectPath = projectPath);
616+
}
496617
services.AddLogging(lb =>
497618
{
498619
lb.
File renamed without changes.
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
// Used by HttpNodeJSServiceIntegrationTests
2+
module.exports = 10;

test/NodeJS/Javascript/package.json

+9
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
{
2+
"private": true,
3+
"scripts": {
4+
"build": "echo ------ Yarn Install ------ && yarn install"
5+
},
6+
"dependencies": {
7+
"prismjs": "^1.17.1"
8+
}
9+
}

test/NodeJS/Javascript/yarn.lock

+41
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY.
2+
# yarn lockfile v1
3+
4+
5+
clipboard@^2.0.0:
6+
version "2.0.4"
7+
resolved "https://registry.npmjs.org/clipboard/-/clipboard-2.0.4.tgz#836dafd66cf0fea5d71ce5d5b0bf6e958009112d"
8+
integrity sha512-Vw26VSLRpJfBofiVaFb/I8PVfdI1OxKcYShe6fm0sP/DtmiWQNCjhM/okTvdCo0G+lMMm1rMYbk4IK4x1X+kgQ==
9+
dependencies:
10+
good-listener "^1.2.2"
11+
select "^1.1.2"
12+
tiny-emitter "^2.0.0"
13+
14+
delegate@^3.1.2:
15+
version "3.2.0"
16+
resolved "https://registry.npmjs.org/delegate/-/delegate-3.2.0.tgz#b66b71c3158522e8ab5744f720d8ca0c2af59166"
17+
integrity sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==
18+
19+
good-listener@^1.2.2:
20+
version "1.2.2"
21+
resolved "https://registry.npmjs.org/good-listener/-/good-listener-1.2.2.tgz#d53b30cdf9313dffb7dc9a0d477096aa6d145c50"
22+
integrity sha1-1TswzfkxPf+33JoNR3CWqm0UXFA=
23+
dependencies:
24+
delegate "^3.1.2"
25+
26+
prismjs@^1.17.1:
27+
version "1.17.1"
28+
resolved "https://registry.npmjs.org/prismjs/-/prismjs-1.17.1.tgz#e669fcbd4cdd873c35102881c33b14d0d68519be"
29+
integrity sha512-PrEDJAFdUGbOP6xK/UsfkC5ghJsPJviKgnQOoxaDbBjwc8op68Quupwt1DeAFoG8GImPhiKXAvvsH7wDSLsu1Q==
30+
optionalDependencies:
31+
clipboard "^2.0.0"
32+
33+
select@^1.1.2:
34+
version "1.1.2"
35+
resolved "https://registry.npmjs.org/select/-/select-1.1.2.tgz#0e7350acdec80b1108528786ec1d4418d11b396d"
36+
integrity sha1-DnNQrN7ICxEIUoeG7B1EGNEbOW0=
37+
38+
tiny-emitter@^2.0.0:
39+
version "2.1.0"
40+
resolved "https://registry.npmjs.org/tiny-emitter/-/tiny-emitter-2.1.0.tgz#1d1a56edfc51c43e863cbb5382a72330e3555423"
41+
integrity sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==

test/NodeJS/Jering.Javascript.NodeJS.Tests.csproj

+18-15
Original file line numberDiff line numberDiff line change
@@ -5,43 +5,46 @@
55
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.1</TargetFrameworks>
66
<IsPackable>false</IsPackable>
77
<IsTestProject>true</IsTestProject>
8-
</PropertyGroup>
9-
10-
<PropertyGroup>
118
<CodeAnalysisRuleSet>../../Jering.Javascript.NodeJS.ruleset</CodeAnalysisRuleSet>
9+
<DefaultItemExcludes>Javascript\node_modules\**;$(DefaultItemExcludes)</DefaultItemExcludes>
1210
</PropertyGroup>
1311

1412
<ItemGroup>
1513
<PackageReference Include="coverlet.msbuild" Version="2.7.0">
16-
<PrivateAssets>all</PrivateAssets>
17-
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
14+
<PrivateAssets>all</PrivateAssets>
15+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
1816
</PackageReference>
1917
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
2018
<PackageReference Include="Moq" Version="4.13.1" />
2119
<PackageReference Include="Roslynator.Analyzers" Version="2.2.0">
22-
<PrivateAssets>all</PrivateAssets>
23-
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
20+
<PrivateAssets>all</PrivateAssets>
21+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
2422
</PackageReference>
2523
<PackageReference Include="Roslynator.CodeFixes" Version="2.0.0">
26-
<PrivateAssets>all</PrivateAssets>
27-
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
24+
<PrivateAssets>all</PrivateAssets>
25+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
2826
</PackageReference>
2927
<PackageReference Include="xunit" Version="2.4.1" />
3028
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
31-
<PrivateAssets>all</PrivateAssets>
32-
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
29+
<PrivateAssets>all</PrivateAssets>
30+
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
3331
</PackageReference>
3432
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
3533
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
34+
<PackageReference Include="Yarn.MSBuild" Version="1.16.0" />
3635
</ItemGroup>
3736

3837
<ItemGroup>
39-
<Content Include=".\dummyModule.js" copyToOutputDirectory="PreserveNewest" />
40-
<EmbeddedResource Include=".\dummyEmbed.txt" LogicalName="dummyEmbed" />
38+
<ProjectReference Include="..\..\src\NodeJS\Jering.Javascript.NodeJS.csproj" />
4139
</ItemGroup>
42-
40+
4341
<ItemGroup>
44-
<ProjectReference Include="..\..\src\NodeJS\Jering.Javascript.NodeJS.csproj" />
42+
<Content Include=".\Javascript\dummyModule.js" copyToOutputDirectory="PreserveNewest" />
43+
<EmbeddedResource Include=".\dummyEmbed.txt" LogicalName="dummyEmbed" />
4544
</ItemGroup>
4645

46+
<Target Name="YarnInstall" BeforeTargets="DispatchToInnerBuilds;PreBuildEvent">
47+
<Yarn WorkingDirectory=".\Javascript" Command="run build" />
48+
</Target>
49+
4750
</Project>

test/NodeJS/StaticNodeJSServiceIntegrationTests.cs

+2
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,8 @@ public async void InvokeFromFileAsync_InvokesJavascript()
139139
{
140140
// Arrange
141141
const string dummyResultString = "success";
142+
StaticNodeJSService.
143+
Configure<NodeJSProcessOptions>(options => options.ProjectPath = "./Javascript");
142144

143145
// Act
144146
DummyResult result = await StaticNodeJSService.

0 commit comments

Comments
 (0)