How can I debug swift with nodejs at the runtime
Maybe how can I debug the libray at the runtime with source code
AFAICT there is no way to use a debugger on the Swift side when calling from node.js. I found that being able to log from Swift to the node console was useful for me in debugging - at least as useful as inserting print statements anway. You can do this using the NodeClass...
import NodeAPI
/// A facade for the NodeConsole class used in node.js.
@NodeClass final class NodeConsoleFacade {
/// The queue to run the callback on (see https://github.com/kabiroberai/node-swift/issues/17)
private let nodeQueue: NodeAsyncQueue
/// Initialize the `nodeQueue` callback queue when the instance is created on the node.js side.
@NodeActor
@NodeConstructor
public init() throws {
nodeQueue = try NodeAsyncQueue(label: "nodeConsoleQueue")
NodeConsole.init(nodeQueue: nodeQueue)
}
/// Initialize the NodeConsole singleton that will execute `callback` on `nodeQueue`.
@NodeActor
@NodeMethod
public func registerLogCallback(callback: NodeFunction) throws {
NodeConsole.console?.setCallback(callback)
}
}
/// A class that can be used to show a message in the node.js console via `NodeConsole.log`.
public final class NodeConsole {
private let nodeQueue: NodeAsyncQueue?
fileprivate var callback: NodeFunction? = nil
/// The singleton `NodeConsole`.
@NodeActor
fileprivate static var console: NodeConsole?
/// Log a message in the node.js console using the `console` singleton.
public static func log(_ message: String) throws {
Task { @NodeActor in
try console?.log(message)
}
}
/// Initialize the NodeConsole singleton when the callback is registered from node.js.
@NodeActor
@discardableResult
fileprivate init(nodeQueue: NodeAsyncQueue) {
self.nodeQueue = nodeQueue
Self.console = self
}
@NodeActor
fileprivate func setCallback(_ callback: NodeFunction) {
self.callback = callback
}
/// Run the `callback` on the `nodeQueue`, passing the `message`.
private func log(_ message: String) throws {
guard let nodeQueue, let callback else { return }
try nodeQueue.run {
try callback.call([message])
}
}
}
You need to expose the facade in your NodeExports:
#NodeModule(exports: [
"test": try NodeFunction { _ in
try NodeConsole.log("I'm going to do something.")
// Do something
try NodeConsole.log("I did something.")
return // Need to return or the compiler becomes confused
},
"NodeConsole": NodeConsoleFacade.deferredConstructor,
])
On the node side, you need to register the callback:
const nodeConsole = new NodeConsole();
nodeConsole.registerLogCallback((message) => {
console.log("Swift> " + message)
});
The test function in the exports shows how you would use NodeConsole.log from Swift.
It's occasionally buggy (as LLDB always tends to be), but you can attach to the Node.js process from LLDB/Xcode and debug your source files. If you're using Xcode, you can do this with Debug > Attach to Process by PID or Name > [enter PID] > Attach.
FWIW, I formalized the NodeConsole.log stuff above into a package you can use in a NodeSwift project, and I included a backend for SwiftLog so that normal logging like logger.info("message") sends data back to node in a callback. Details at NodeSwiftLogging.