Skip to content

Fix requiring modules from in-memory modules #59

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 5 commits into from
Nov 29, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
9 changes: 1 addition & 8 deletions perf/NodeJS/Jering.Javascript.NodeJS.Performance.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -24,14 +24,7 @@
<ProjectReference Include="..\..\src\NodeJS\Jering.Javascript.NodeJS.csproj" />
</ItemGroup>

<ItemGroup>
<JavascriptInputs Include="package.json" />
<JavascriptOutputs Include="yarn.lock" />
<!-- 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 -->
<UpToDateCheckInput Include="@(JavascriptInputs)" />
</ItemGroup>

<Target Name="YarnInstall" BeforeTargets="DispatchToInnerBuilds;PreBuildEvent" Inputs="@(JavascriptInputs)" Outputs="@(JavascriptOutputs)">
<Target Name="YarnInstall" BeforeTargets="DispatchToInnerBuilds;PreBuildEvent">
<Yarn WorkingDirectory="." Command="run build" />
</Target>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down
6 changes: 6 additions & 0 deletions src/NodeJS/Javascript/Typings/extensions.d.ts
Original file line number Diff line number Diff line change
@@ -1 +1,7 @@
declare function __non_webpack_require__(path: string): any;

declare module NodeJS {
interface Global {
module: Module;
}
}
127 changes: 124 additions & 3 deletions test/NodeJS/HttpNodeJSServiceIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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 <test project path>/bin/debug/<framework>

// 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<string>(memoryStream, args: new[] { dummyCode }).ConfigureAwait(false);
}

// Assert
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>
<span class=""token punctuation"">{</span>
<span class=""token comment"">// Example comment</span>
<span class=""token keyword"">return</span> arg <span class=""token operator"">+</span> <span class=""token string"">""dummyString""</span><span class=""token punctuation"">;</span>
<span class=""token punctuation"">}</span>";
Assert.Equal(expectedResult, result);
}

[Fact(Timeout = _timeoutMS)]
public async void InvokeFromStreamAsync_LoadsRequiredModuleFromFileInProjectDirectory()
{
// Arrange
HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is <test project path>/bin/debug/<framework>

// 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<int>(memoryStream).ConfigureAwait(false);
}

// Assert
Assert.Equal(10, result); // dummyReturnsValueModule.js just exports 10
}

[Fact(Timeout = _timeoutMS)]
public void InvokeFromStreamAsync_IsThreadSafe()
{
Expand Down Expand Up @@ -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 <test project path>/bin/debug/<framework>

// Act
string result = await testSubject.InvokeFromStringAsync<string>(@"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 = @"<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>
<span class=""token punctuation"">{</span>
<span class=""token comment"">// Example comment</span>
<span class=""token keyword"">return</span> arg <span class=""token operator"">+</span> <span class=""token string"">""dummyString""</span><span class=""token punctuation"">;</span>
<span class=""token punctuation"">}</span>";
Assert.Equal(expectedResult, result);
}

[Fact(Timeout = _timeoutMS)]
public async void InvokeFromStringAsync_LoadsRequiredModuleFromFileInProjectDirectory()
{
// Arrange
HttpNodeJSService testSubject = CreateHttpNodeJSService(projectPath: Directory.GetCurrentDirectory() + "/../../../Javascript"); // Current directory is <test project path>/bin/debug/<framework>

// Act
int result = await testSubject.InvokeFromStringAsync<int>(@"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()
{
Expand Down Expand Up @@ -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.
Expand All @@ -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<DummyResult>();
Expand Down Expand Up @@ -489,10 +605,15 @@ public static IEnumerable<object[]> AllInvokeMethods_ReceiveAndLogStderrOutput_D
/// <summary>
/// Specify <paramref name="loggerStringBuilder"/> for access to all logging output.
/// </summary>
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<NodeJSProcessOptions>(options => options.ProjectPath = projectPath);
}
services.AddLogging(lb =>
{
lb.
Expand Down
File renamed without changes.
2 changes: 2 additions & 0 deletions test/NodeJS/Javascript/dummyReturnsValueModule.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
// Used by HttpNodeJSServiceIntegrationTests
module.exports = 10;
9 changes: 9 additions & 0 deletions test/NodeJS/Javascript/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
{
"private": true,
"scripts": {
"build": "echo ------ Yarn Install ------ && yarn install"
},
"dependencies": {
"prismjs": "^1.17.1"
}
}
41 changes: 41 additions & 0 deletions test/NodeJS/Javascript/yarn.lock
Original file line number Diff line number Diff line change
@@ -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==
33 changes: 18 additions & 15 deletions test/NodeJS/Jering.Javascript.NodeJS.Tests.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -5,43 +5,46 @@
<TargetFrameworks Condition="'$(OS)' != 'Windows_NT'">netcoreapp2.1</TargetFrameworks>
<IsPackable>false</IsPackable>
<IsTestProject>true</IsTestProject>
</PropertyGroup>

<PropertyGroup>
<CodeAnalysisRuleSet>../../Jering.Javascript.NodeJS.ruleset</CodeAnalysisRuleSet>
<DefaultItemExcludes>Javascript\node_modules\**;$(DefaultItemExcludes)</DefaultItemExcludes>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="coverlet.msbuild" Version="2.7.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="16.4.0" />
<PackageReference Include="Moq" Version="4.13.1" />
<PackageReference Include="Roslynator.Analyzers" Version="2.2.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Roslynator.CodeFixes" Version="2.0.0">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="xunit" Version="2.4.1" />
<PackageReference Include="xunit.runner.visualstudio" Version="2.4.1">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers</IncludeAssets>
</PackageReference>
<PackageReference Include="Microsoft.Extensions.DependencyInjection" Version="3.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Debug" Version="3.0.0" />
<PackageReference Include="Yarn.MSBuild" Version="1.16.0" />
</ItemGroup>

<ItemGroup>
<Content Include=".\dummyModule.js" copyToOutputDirectory="PreserveNewest" />
<EmbeddedResource Include=".\dummyEmbed.txt" LogicalName="dummyEmbed" />
<ProjectReference Include="..\..\src\NodeJS\Jering.Javascript.NodeJS.csproj" />
</ItemGroup>

<ItemGroup>
<ProjectReference Include="..\..\src\NodeJS\Jering.Javascript.NodeJS.csproj" />
<Content Include=".\Javascript\dummyModule.js" copyToOutputDirectory="PreserveNewest" />
<EmbeddedResource Include=".\dummyEmbed.txt" LogicalName="dummyEmbed" />
</ItemGroup>

<Target Name="YarnInstall" BeforeTargets="DispatchToInnerBuilds;PreBuildEvent">
<Yarn WorkingDirectory=".\Javascript" Command="run build" />
</Target>

</Project>
2 changes: 2 additions & 0 deletions test/NodeJS/StaticNodeJSServiceIntegrationTests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -139,6 +139,8 @@ public async void InvokeFromFileAsync_InvokesJavascript()
{
// Arrange
const string dummyResultString = "success";
StaticNodeJSService.
Configure<NodeJSProcessOptions>(options => options.ProjectPath = "./Javascript");

// Act
DummyResult result = await StaticNodeJSService.
Expand Down