diff --git a/CHANGELOG.md b/CHANGELOG.md index 188ee8c..1a2349c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,21 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/) and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html). +## [3.1.3] + +### Added +- Added `debug_mode` configuration option to handle Node.js stdout/stderr messages +- Implemented proper `handle_info/2` callback to handle messages from Node.js processes +- Added safeguards to reset_terminal to prevent errors during termination with invalid ports + +### Fixed +- Fixed "unexpected message in handle_info/2" errors when Node.js emits debug messages +- Fixed potential crashes during termination when a port becomes invalid + +### Contributors +- @francois-codes for the initial implementation +- Revelry team for refinements + ## [3.1.2] ### Changed diff --git a/README.md b/README.md index 07b17ce..df81bea 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,22 @@ directory containing your JavaScript modules. supervisor(NodeJS, [[path: "/node_app_root", pool_size: 4]]) ``` +### Debug Mode + +When working with Node.js applications, you may encounter debug messages or warnings from the Node.js runtime, especially when using inspector or debugging tools. To properly handle these messages: + +```elixir +# In your config/dev.exs or other appropriate config file +config :nodejs, debug_mode: true +``` + +When `debug_mode` is enabled: +- Node.js stdout/stderr messages will be logged at the info level +- Messages like "Debugger listening on..." will not cause errors +- All Node.js processes will log their output through Elixir's Logger + +This is particularly useful during development or when debugging Node.js integration issues. + ### Calling JavaScript module functions with `NodeJS.call(module, args \\ [])`. If the module exports a function directly, like this: @@ -111,4 +127,4 @@ module.exports = async function echo(x, delay = 1000) { } ``` -https://github.com/revelrylabs/elixir-nodejs/blob/master/test/js/slow-async-echo.js \ No newline at end of file +https://github.com/revelrylabs/elixir-nodejs/blob/master/test/js/slow-async-echo.js diff --git a/lib/nodejs/worker.ex b/lib/nodejs/worker.ex index cb8dc40..199a2c7 100644 --- a/lib/nodejs/worker.ex +++ b/lib/nodejs/worker.ex @@ -124,21 +124,28 @@ defmodule NodeJS.Worker do end end - defp env do - Mix.env() - rescue - _ -> :release + # Determines if debug mode is enabled via application configuration + defp debug_mode? do + Application.get_env(:nodejs, :debug_mode, false) end - def handle_info({_pid, data}, state) do - with :dev <- env(), - {_, {:eol, msg}} <- data do + # Handles any messages from the Node.js process + # When debug_mode is enabled, these messages (like Node.js debug info) + # will be logged at info level + @doc false + def handle_info({_pid, {:data, {_flag, msg}}}, state) do + if debug_mode?() do Logger.info("NodeJS: #{msg}") end {:noreply, state} end + # Catch-all handler for other messages + def handle_info(_message, state) do + {:noreply, state} + end + defp decode(data) do data |> to_string() @@ -149,9 +156,16 @@ defmodule NodeJS.Worker do end end + # Safely resets the terminal, handling potential errors if + # the port is already closed or invalid defp reset_terminal(port) do - Port.command(port, "\x1b[0m\x1b[?7h\x1b[?25h\x1b[H\x1b[2J") - Port.command(port, "\x1b[!p\x1b[?47l") + try do + Port.command(port, "\x1b[0m\x1b[?7h\x1b[?25h\x1b[H\x1b[2J") + Port.command(port, "\x1b[!p\x1b[?47l") + rescue + _ -> + Logger.debug("NodeJS: Could not reset terminal - port may be closed") + end end @doc false diff --git a/mix.exs b/mix.exs index 6a779f2..4fb1eeb 100644 --- a/mix.exs +++ b/mix.exs @@ -4,7 +4,7 @@ defmodule NodeJS.MixProject do def project do [ app: :nodejs, - version: "3.1.2", + version: "3.1.3", elixir: "~> 1.12", start_permanent: Mix.env() == :prod, deps: deps(), diff --git a/test/nodejs_test.exs b/test/nodejs_test.exs index 928e180..713e5f3 100644 --- a/test/nodejs_test.exs +++ b/test/nodejs_test.exs @@ -277,4 +277,45 @@ defmodule NodeJS.Test do assert Enum.all?(results, &match?({:ok, "clean output"}, &1)) end end + + describe "debug mode" do + test "handles debug messages without crashing" do + File.mkdir_p!("test/js/debug_test") + + File.write!("test/js/debug_test/debug_logger.js", """ + // This file outputs debugging information to stdout + console.log("Debug message: Module loading"); + console.debug("Debug message: Initializing module"); + + module.exports = function testFunction(input) { + console.log(`Debug message: Function called with input: ${input}`); + console.debug("Debug message: Processing input"); + + return `Processed: ${input}`; + }; + """) + + # With debug_mode disabled, function still works despite debug output + result = NodeJS.call("debug_test/debug_logger", ["test input"]) + assert {:ok, "Processed: test input"} = result + + # Enable debug_mode to verify it works in that mode too + original_setting = Application.get_env(:nodejs, :debug_mode) + Application.put_env(:nodejs, :debug_mode, true) + + # Function still works with debug_mode enabled + result = NodeJS.call("debug_test/debug_logger", ["test input"]) + assert {:ok, "Processed: test input"} = result + + # Restore original setting + if is_nil(original_setting) do + Application.delete_env(:nodejs, :debug_mode) + else + Application.put_env(:nodejs, :debug_mode, original_setting) + end + + # Clean up + File.rm!("test/js/debug_test/debug_logger.js") + end + end end