Skip to content
Open
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
49 changes: 21 additions & 28 deletions Sources/JXKit/JXContext.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,23 @@ open class JXContext {
return scriptLoader.didChange != nil
}

/// Configure a global script loader to use as the default when no loader is provided to the `Configuration`.
///
/// - Seealso: ``JXContext/Configuration/scriptLoader``
public static var defaultScriptLoader: JXScriptLoader = DefaultScriptLoader()

/// The script loader to use for loading JavaScript script files. If the loader vends a non-nil `didChange` listener collection, dynamic reloading will be enabled.
public var scriptLoader: JXScriptLoader

/// Configure a global logging function for JX log messages. Defaults to using `print`.
public static var defaultLog: (String) -> Void = { print($0) }

/// The logging function to use for JX log messages.
public var log: (String) -> Void
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Added very simple configurable log function that defaults to 'print', and now any messages we log (and by default our SwiftUI error handling) use this.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

How about log: (Any...) -> Void, which would let us use this for a shim for console.log as well?


public init(strict: Bool = true, scriptLoader: JXScriptLoader? = nil, log: @escaping (String) -> Void = { print($0) }) {
public init(strict: Bool = true, scriptLoader: JXScriptLoader = Self.defaultScriptLoader, log: @escaping (String) -> Void = Self.defaultLog) {
self.strict = strict
self.scriptLoader = scriptLoader ?? DefaultScriptLoader()
self.scriptLoader = scriptLoader
self.log = log
}
}
Expand Down Expand Up @@ -124,7 +132,7 @@ extension JXContext {
return try scriptManager.evalClosure(source: script, type: .inline, withArguments: arguments, this: this, root: root)
}

/// Evaluate the given JavaScript as a closure, giving it its own scope for local functions and vars.
/// Evaluate the given JavaScript as a clsoure, giving it its own scope for local functions and vars.
///
/// - Parameters:
/// - script: The JavaScript code to evaluate.
Expand Down Expand Up @@ -185,7 +193,16 @@ extension JXContext {

/// Internal function called by the `ScriptManager` to evaluate JavaScript code.
func evalInternal(script: String, this: JXValue?) throws -> JXValue {
try initializeEval()
if configuration.strict == true && !strictEvaluated {
let useStrict = "\"use strict\";\n" // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
let script = useStrict.withCString(JSStringCreateWithUTF8CString)
defer { JSStringRelease(script) }
let _ = try trying {
JSEvaluateScript(contextRef, script, nil, nil, 0, $0)
}
strictEvaluated = true
}

do {
// Allow SPI to perform pre-eval actions or even e.g. execute macros
if let spiResult = try spi?.eval(script, this: this, in: self) {
Expand All @@ -202,30 +219,6 @@ extension JXContext {
} catch {
throw JXError(cause: error, script: script)
}
}

private func initializeEval() throws {
guard !evalInitialized else {
return
}
if configuration.strict {
let useStrict = "\"use strict\";\n" // https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Strict_mode
let script = useStrict.withCString(JSStringCreateWithUTF8CString)
defer { JSStringRelease(script) }
let _ = try trying {
JSEvaluateScript(contextRef, script, nil, nil, 0, $0)
}
}
let log = JXValue(newFunctionIn: self) { [weak self] context, this, args in
guard let self else {
return context.undefined()
}
guard args.count == 1 else {
throw JXError(message: "'console.log' expects a single argument")
}
try self.configuration.log(args[0].string)
return self.undefined()
}
try global["console"].setProperty("log", log)
evalInitialized = true
}
Expand Down
6 changes: 3 additions & 3 deletions Sources/JXKit/JXError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -74,15 +74,15 @@ public struct JXError: Error, CustomStringConvertible, @unchecked Sendable {
return JXError(message: "Expected a JavaScript function but received '\(value)'")
}

public static func valueNotPromise(_ value: JXValue) -> JXError {
@inlinable static func valueNotPromise(_ value: JXValue) -> JXError {
return JXError(message: "Expected a JavaScript Promise but received '\(value)'")
}

public static func valueNotSymbol(_ value: JXValue) -> JXError {
@inlinable public static func valueNotSymbol(_ value: JXValue) -> JXError {
return JXError(message: "Expected a JavaScript symbol but received '\(value)'")
}

static func invalidNumericConversion(_ value: JXValue, to number: Double) -> JXError {
@inlinable static func invalidNumericConversion(_ value: JXValue, to number: Double) -> JXError {
return JXError(message: "JavaScript value '\(value)' converted to invalid number '\(number)'")
}

Expand Down
Loading