GithubHelp home page GithubHelp logo

Comments (7)

JeremyTCD avatar JeremyTCD commented on August 19, 2024

Hey! We got a direct email on this issue 3 weeks back. Out of curiosity, was that you? Asking to ascertain if this is the second occurrence of this issue. That would strongly suggest the problem is with our library. The console window should not open.

Anyway I was unable to reproduce the problem that time. Is it possible for you to create a minimal reproduction?
So create a new project with the same configuration as yours and call InvokeFromFileAsync on a dummy js file. Then push the repro to Github.

Would be more than happy to look at it and figure this out.

from javascript.nodejs.

hansooms999 avatar hansooms999 commented on August 19, 2024

No the email wasn't me, I'll let you know when I get a sample repo up to more easily showcase the problem.

Thank you for the quick response!

from javascript.nodejs.

JeremyTCD avatar JeremyTCD commented on August 19, 2024

After some digging, I think the following change to this library may fix the issue:

var startInfo = new ProcessStartInfo("node")
{
Arguments = $"{_nodeJSProcessOptions.NodeAndV8Options} -e \"{nodeServerScript}\" -- --parentPid {currentProcessPid} --port {_nodeJSProcessOptions.Port}",
UseShellExecute = false,
RedirectStandardInput = true,
RedirectStandardOutput = true,
RedirectStandardError = true,
WorkingDirectory = _nodeJSProcessOptions.ProjectPath
};

Changed to:

var startInfo = new ProcessStartInfo("node")
{
    Arguments = $"{_nodeJSProcessOptions.NodeAndV8Options} -e \"{nodeServerScript}\" -- --parentPid {currentProcessPid} --port {_nodeJSProcessOptions.Port}",
    UseShellExecute = false,
    RedirectStandardInput = true,
    RedirectStandardOutput = true,
    RedirectStandardError = true,
    WorkingDirectory = _nodeJSProcessOptions.ProjectPath,
    /*** Possible solution for console window issue ***/
    CreateNoWindow = true
};

Not 100% sure if this fixes the issue, but worth trying. If you could test it out I'd appreciate it. Steps:

  1. Add the following CustomNodeJSProcessFactory.cs file to your project. It's identical to the original class that creates NodeJS processes except it sets the property mentioned above. You might have to add several packages to your project temporarily and you'll have to enter your namespace:

    using Jering.Javascript.NodeJS;
    using Microsoft.Extensions.Options;
    using System;
    using System.Diagnostics;
    using System.Text;
    
    namespace <your namespace>
    {
        /// <summary>
        /// Custom <see cref="INodeJSProcessFactory"/> implementation.
        /// </summary>
        public class CustomNodeJSProcessFactory : INodeJSProcessFactory
        {
            private readonly NodeJSProcessOptions _nodeJSProcessOptions;
    
            /// <summary>
            /// Creates a <see cref="NodeJSProcessFactory"/>.
            /// </summary>
            /// <param name="optionsAccessor"></param>
            public CustomNodeJSProcessFactory(IOptions<NodeJSProcessOptions> optionsAccessor)
            {
                _nodeJSProcessOptions = optionsAccessor?.Value ?? new NodeJSProcessOptions();
            }
    
            /// <inheritdoc />
            public INodeJSProcess Create(string serverScript)
            {
                Console.WriteLine("Creating process using custom NodeJSProcessFactory");
    
                ProcessStartInfo startInfo = CreateStartInfo(serverScript);
    
                return new NodeJSProcess(CreateProcess(startInfo));
            }
    
            internal ProcessStartInfo CreateStartInfo(string nodeServerScript)
            {
                nodeServerScript = EscapeCommandLineArg(nodeServerScript); // TODO can we escape before embedding? Would avoid an allocation every time we start a NodeJS process.
    
                int currentProcessPid = Process.GetCurrentProcess().Id;
                var startInfo = new ProcessStartInfo("node")
                {
                    Arguments = $"{_nodeJSProcessOptions.NodeAndV8Options} -e \"{nodeServerScript}\" -- --parentPid {currentProcessPid} --port {_nodeJSProcessOptions.Port}",
                    UseShellExecute = false,
                    RedirectStandardInput = true,
                    RedirectStandardOutput = true,
                    RedirectStandardError = true,
                    WorkingDirectory = _nodeJSProcessOptions.ProjectPath,
                    /*** Possible solution for console window issue ***/
                    CreateNoWindow = true
                };
    
                // Append environment Variables
                if (_nodeJSProcessOptions.EnvironmentVariables != null)
                {
                    foreach (var envVarKey in _nodeJSProcessOptions.EnvironmentVariables.Keys)
                    {
                        string envVarValue = _nodeJSProcessOptions.EnvironmentVariables[envVarKey];
                        if (envVarValue != null)
                        {
                            startInfo.Environment[envVarKey] = envVarValue;
                        }
                    }
                }
    
                return startInfo;
            }
    
            internal Process CreateProcess(ProcessStartInfo startInfo)
            {
                try
                {
                    Process process = Process.Start(startInfo);
    
                    // On Mac at least, a killed child process is left open as a zombie until the parent
                    // captures its exit code. We don't need the exit code for this process, and don't want
                    // to use process.WaitForExit() explicitly (we'd have to block the thread until it really
                    // has exited), but we don't want to leave zombies lying around either. It's sufficient
                    // to use process.EnableRaisingEvents so that .NET will grab the exit code and let the
                    // zombie be cleaned away without having to block our thread.
                    process.EnableRaisingEvents = true;
    
                    return process;
                }
                catch (Exception exception)
                {
                    throw new InvalidOperationException("Failed to start NodeJS process", exception);
                }
            }
    
            internal string EscapeCommandLineArg(string arg)
            {
                var stringBuilder = new StringBuilder();
                int slashSequenceLength = 0;
                for (int i = 0; i < arg.Length; i++)
                {
                    char currentChar = arg[i];
    
                    if (currentChar == '\\')
                    {
                        slashSequenceLength++;
    
                        // If the last character in the argument is \, it must be escaped, together with any \ that immediately preceed it.
                        // This prevents situations like: SomeExecutable.exe "SomeArg\", where the quote meant to demarcate the end of the
                        // argument gets escaped.
                        if (i == arg.Length - 1)
                        {
                            for (int j = 0; j < slashSequenceLength; j++)
                            {
                                stringBuilder.
                                    Append('\\').
                                    Append('\\');
                            }
                        }
                    }
                    else if (currentChar == '"')
                    {
                        // Every \ or sequence of \ that preceed a " must be escaped.
                        for (int j = 0; j < slashSequenceLength; j++)
                        {
                            stringBuilder.
                                Append('\\').
                                Append('\\');
                        }
                        slashSequenceLength = 0;
    
                        stringBuilder.
                            Append('\\').
                            Append('"');
                    }
                    else
                    {
                        for (int j = 0; j < slashSequenceLength; j++)
                        {
                            stringBuilder.Append('\\');
                        }
                        slashSequenceLength = 0;
    
                        stringBuilder.Append(currentChar);
                    }
                }
    
                return stringBuilder.ToString();
            }
        }
    }
  2. When you add Jering.Javascript.NodeJS to your services, overwrite the default INodeJSProcessFactory service:

    ServiceCollection services = new ServiceCollection();
    
    services.AddNodeJS();
    // Override the default service
    services.AddSingleton<INodeJSProcessFactory, CustomNodeJSProcessFactory>();

    If you're using the static API you'd do it like this:

     ServiceCollection services = new ServiceCollection();
    
     services.AddNodeJS();
     // Override the default service
     services.AddSingleton<INodeJSProcessFactory, CustomNodeJSProcessFactory>();
    
    StaticNodeJSService.SetServices(services);
    
    // Subsequent calls to the static API will use CustomNodeJSProcessFactory

Let me know if this fixes the issue. If it does there's no need for a repro!

from javascript.nodejs.

hansooms999 avatar hansooms999 commented on August 19, 2024

Just got back to this, have not tried it out yet but just set up a sample repo. I will look to see if this fixes it but for now here's the sample repo. https://github.com/hansooms999/JeringConsoleSample/

I'll let you know if the issue is fixed. Thank you!

from javascript.nodejs.

JeremyTCD avatar JeremyTCD commented on August 19, 2024

Just tested your repro with the fix and it works properly now! Will publish a new version of Jering.Javascript.NodeJS today.

from javascript.nodejs.

JeremyTCD avatar JeremyTCD commented on August 19, 2024

@hansooms999 Fixed version: 5.4.4 🎉. Thanks for reporting the issue. Let me know if you have any trouble with the new version.

from javascript.nodejs.

hansooms999 avatar hansooms999 commented on August 19, 2024

@JeremyTCD This fixed the issue perfectly. Thanks for your help! 🎉

from javascript.nodejs.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.