├── .gitignore ├── LICENSE ├── Package.swift ├── Sources ├── NAPI │ ├── Arguments.swift │ ├── Class.swift │ ├── Error.swift │ ├── Function.swift │ ├── NAPI.swift │ ├── Null.swift │ ├── PropertyDescriptor.swift │ ├── RunLoop.swift │ ├── Undefined.swift │ ├── Value.swift │ ├── ValueConvertible.swift │ └── Wrap.swift └── NAPIC │ ├── include │ ├── NAPI.h │ ├── node_api.h │ └── node_api_types.h │ └── source.c ├── Tests ├── .gitignore ├── Package.swift ├── Sources │ ├── NAPITests │ │ └── NAPITests.swift │ └── Trampoline │ │ ├── include │ │ └── Trampoline.h │ │ └── trampoline.c ├── index.js ├── package.json └── test │ ├── errors.js │ ├── optional-values.js │ ├── return-values.js │ └── take-values.js └── readme.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | /.build 3 | /Packages 4 | /*.xcodeproj 5 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019-2020 Linus Unnebäck 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "NAPI", 7 | products: [ 8 | .library(name: "NAPI", type: .static, targets: ["NAPI"]), 9 | .library(name: "NAPIC", type: .static, targets: ["NAPIC"]), 10 | ], 11 | targets: [ 12 | .target(name: "NAPI", dependencies: ["NAPIC"]), 13 | .target(name: "NAPIC", publicHeadersPath: "include"), 14 | ] 15 | ) 16 | -------------------------------------------------------------------------------- /Sources/NAPI/Arguments.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | 3 | internal typealias NullableArguments = ( 4 | // Values 5 | napi_value?, 6 | napi_value?, 7 | napi_value?, 8 | napi_value?, 9 | napi_value?, 10 | napi_value?, 11 | napi_value?, 12 | napi_value?, 13 | napi_value?, 14 | napi_value?, 15 | 16 | // Number of passed arguments 17 | length: Int, 18 | 19 | // The `this` value 20 | this: napi_value? 21 | ) 22 | 23 | public typealias Arguments = ( 24 | // Values 25 | napi_value, 26 | napi_value, 27 | napi_value, 28 | napi_value, 29 | napi_value, 30 | napi_value, 31 | napi_value, 32 | napi_value, 33 | napi_value, 34 | napi_value, 35 | 36 | // Number of passed arguments 37 | length: Int, 38 | 39 | // The `this` value 40 | this: napi_value 41 | ) 42 | -------------------------------------------------------------------------------- /Sources/NAPI/Class.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | 3 | fileprivate func defineClass(_ env: napi_env, named name: String, _ constructor: @escaping Callback, _ properties: [PropertyDescriptor]) throws -> napi_value { 4 | var result: napi_value? 5 | let nameData = name.data(using: .utf8)! 6 | let props = try properties.map { try $0.value(env) } 7 | 8 | let data = CallbackData(callback: constructor) 9 | let dataPointer = Unmanaged.passRetained(data).toOpaque() 10 | 11 | let status = nameData.withUnsafeBytes { nameBytes in 12 | props.withUnsafeBufferPointer { propertiesBytes in 13 | napi_define_class(env, nameBytes, nameData.count, swiftNAPICallback, dataPointer, properties.count, propertiesBytes.baseAddress, &result) 14 | } 15 | } 16 | 17 | guard status == napi_ok else { throw NAPI.Error(status) } 18 | 19 | return result! 20 | } 21 | 22 | fileprivate enum InternalClass { 23 | case swift(String, Callback, [PropertyDescriptor]) 24 | case javascript(napi_value) 25 | } 26 | 27 | public class Class: ValueConvertible { 28 | fileprivate let value: InternalClass 29 | 30 | public required init(_ env: napi_env, from: napi_value) throws { 31 | self.value = .javascript(from) 32 | } 33 | 34 | public init(named name: String, _ constructor: @escaping Callback, _ properties: [PropertyDescriptor]) { 35 | self.value = .swift(name, constructor, properties) 36 | } 37 | 38 | public func napiValue(_ env: napi_env) throws -> napi_value { 39 | switch value { 40 | case .swift(let name, let constructor, let properties): return try defineClass(env, named: name, constructor, properties) 41 | case .javascript(let value): return value 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/NAPI/Error.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | 3 | public enum Error: Swift.Error { 4 | case invalidArg 5 | case objectExpected 6 | case stringExpected 7 | case nameExpected 8 | case functionExpected 9 | case numberExpected 10 | case booleanExpected 11 | case arrayExpected 12 | case genericFailure 13 | case pendingException 14 | case cancelled 15 | case escapeCalledTwice 16 | case handleScopeMismatch 17 | case callbackScopeMismatch 18 | case queueFull 19 | case closing 20 | case bigintExpected 21 | 22 | case unknown(UInt32) 23 | 24 | public init(_ napiStatus: napi_status) { 25 | switch napiStatus.rawValue { 26 | case 1: self = .invalidArg 27 | case 2: self = .objectExpected 28 | case 3: self = .stringExpected 29 | case 4: self = .nameExpected 30 | case 5: self = .functionExpected 31 | case 6: self = .numberExpected 32 | case 7: self = .booleanExpected 33 | case 8: self = .arrayExpected 34 | case 9: self = .genericFailure 35 | case 10: self = .pendingException 36 | case 11: self = .cancelled 37 | case 12: self = .escapeCalledTwice 38 | case 13: self = .handleScopeMismatch 39 | case 14: self = .callbackScopeMismatch 40 | case 15: self = .queueFull 41 | case 16: self = .closing 42 | case 17: self = .bigintExpected 43 | default: self = .unknown(napiStatus.rawValue) 44 | } 45 | } 46 | } 47 | 48 | extension Error { 49 | func napi_throw(_ env: napi_env) -> napi_status { 50 | switch self { 51 | case .objectExpected: return napi_throw_type_error(env, nil, "Expected object") 52 | case .stringExpected: return napi_throw_type_error(env, nil, "Expected string") 53 | case .nameExpected: return napi_throw_type_error(env, nil, "Expected Symbol or string") 54 | case .functionExpected: return napi_throw_type_error(env, nil, "Expected function") 55 | case .numberExpected: return napi_throw_type_error(env, nil, "Expected number") 56 | case .booleanExpected: return napi_throw_type_error(env, nil, "Expected boolean") 57 | case .arrayExpected: return napi_throw_type_error(env, nil, "Expected array") 58 | case .bigintExpected: return napi_throw_type_error(env, nil, "Expected BigInt") 59 | default: return napi_throw_error(env, nil, self.localizedDescription) 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /Sources/NAPI/Function.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | 3 | fileprivate func createFunction(_ env: napi_env, named name: String, _ function: @escaping Callback) throws -> napi_value { 4 | var result: napi_value? 5 | let nameData = name.data(using: .utf8)! 6 | 7 | let data = CallbackData(callback: function) 8 | let dataPointer = Unmanaged.passRetained(data).toOpaque() 9 | 10 | let status = nameData.withUnsafeBytes { nameBytes in 11 | napi_create_function(env, nameBytes, nameData.count, swiftNAPICallback, dataPointer, &result) 12 | } 13 | 14 | guard status == napi_ok else { throw NAPI.Error(status) } 15 | 16 | return result! 17 | } 18 | 19 | fileprivate enum InternalFunction { 20 | case javascript(napi_value) 21 | case swift(String, Callback) 22 | } 23 | 24 | public class Function: ValueConvertible { 25 | fileprivate let value: InternalFunction 26 | 27 | public required init(_ env: napi_env, from: napi_value) throws { 28 | self.value = .javascript(from) 29 | } 30 | 31 | public init(named name: String, _ callback: @escaping Callback) { 32 | self.value = .swift(name, callback) 33 | } 34 | 35 | public func napiValue(_ env: napi_env) throws -> napi_value { 36 | switch value { 37 | case .swift(let name, let callback): return try createFunction(env, named: name, callback) 38 | case .javascript(let value): return value 39 | } 40 | } 41 | } 42 | 43 | /* constructor overloads */ 44 | extension Function { 45 | /* (...) -> Void */ 46 | 47 | public convenience init(named name: String, _ callback: @escaping () throws -> Void) { 48 | self.init(named: name, { (_, _) in try callback(); return Value.undefined }) 49 | } 50 | 51 | public convenience init(named name: String, _ callback: @escaping (A) throws -> Void) { 52 | self.init(named: name, { (env, args) in try callback(A(env, from: args.0)); return Value.undefined }) 53 | } 54 | 55 | public convenience init(named name: String, _ callback: @escaping (A, B) throws -> Void) { 56 | self.init(named: name, { (env, args) in try callback(A(env, from: args.0), B(env, from: args.1)); return Value.undefined }) 57 | } 58 | 59 | /* (env, ...) -> Void */ 60 | 61 | public convenience init(named name: String, _ callback: @escaping (napi_env) throws -> Void) { 62 | self.init(named: name, { (env, _) in try callback(env); return Value.undefined }) 63 | } 64 | 65 | public convenience init(named name: String, _ callback: @escaping (napi_env, A) throws -> Void) { 66 | self.init(named: name, { (env, args) in try callback(env, A(env, from: args.0)); return Value.undefined }) 67 | } 68 | 69 | public convenience init(named name: String, _ callback: @escaping (napi_env, A, B) throws -> Void) { 70 | self.init(named: name, { (env, args) in try callback(env, A(env, from: args.0), B(env, from: args.1)); return Value.undefined }) 71 | } 72 | } 73 | 74 | /* call(...) */ 75 | extension Function { 76 | fileprivate func _call(_ env: napi_env, this: napi_value, args: [napi_value?]) throws -> Void { 77 | let handle = try self.napiValue(env) 78 | 79 | let status = args.withUnsafeBufferPointer { argsBytes in 80 | napi_call_function(env, this, handle, args.count, argsBytes.baseAddress, nil) 81 | } 82 | 83 | guard status == napi_ok else { throw NAPI.Error(status) } 84 | } 85 | 86 | fileprivate func _call(_ env: napi_env, this: napi_value, args: [napi_value?]) throws -> Result { 87 | let handle = try self.napiValue(env) 88 | 89 | var result: napi_value? 90 | let status = args.withUnsafeBufferPointer { argsBytes in 91 | napi_call_function(env, this, handle, args.count, argsBytes.baseAddress, &result) 92 | } 93 | 94 | guard status == napi_ok else { throw NAPI.Error(status) } 95 | 96 | return try Result(env, from: result!) 97 | } 98 | 99 | /* (...) -> Void */ 100 | 101 | public func call(_ env: napi_env) throws -> Void { 102 | return try _call(env, this: Value.undefined.napiValue(env), args: []) 103 | } 104 | 105 | public func call(_ env: napi_env, _ a: A) throws -> Void { 106 | return try _call(env, this: Value.undefined.napiValue(env), args: [a.napiValue(env)]) 107 | } 108 | 109 | public func call(_ env: napi_env, _ a: A, _ b: B) throws -> Void { 110 | return try _call(env, this: Value.undefined.napiValue(env), args: [a.napiValue(env), b.napiValue(env)]) 111 | } 112 | 113 | /* (...) -> ValueConvertible */ 114 | 115 | public func call(_ env: napi_env) throws -> Result { 116 | return try _call(env, this: Value.undefined.napiValue(env), args: []) 117 | } 118 | 119 | public func call(_ env: napi_env, _ a: A) throws -> Result { 120 | return try _call(env, this: Value.undefined.napiValue(env), args: [a.napiValue(env)]) 121 | } 122 | 123 | public func call(_ env: napi_env, _ a: A, _ b: B) throws -> Result { 124 | return try _call(env, this: Value.undefined.napiValue(env), args: [a.napiValue(env), b.napiValue(env)]) 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /Sources/NAPI/NAPI.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | 3 | public func strictlyEquals(_ env: napi_env, lhs: napi_value, rhs: napi_value) throws -> Bool { 4 | var isEqual = false 5 | let status = napi_strict_equals(env, lhs, rhs, &isEqual) 6 | guard status == napi_ok else { throw NAPI.Error(status) } 7 | return isEqual 8 | } 9 | 10 | public func strictlyEquals(_ env: napi_env, lhs: napi_value, rhs: ValueConvertible) throws -> Bool { 11 | return try strictlyEquals(env, lhs: lhs, rhs: rhs.napiValue(env)) 12 | } 13 | 14 | public func strictlyEquals(_ env: napi_env, lhs: ValueConvertible, rhs: napi_value) throws -> Bool { 15 | return try strictlyEquals(env, lhs: lhs.napiValue(env), rhs: rhs) 16 | } 17 | 18 | public func strictlyEquals(_ env: napi_env, lhs: ValueConvertible, rhs: ValueConvertible) throws -> Bool { 19 | return try strictlyEquals(env, lhs: lhs.napiValue(env), rhs: rhs.napiValue(env)) 20 | } 21 | 22 | public func defineProperties(_ env: napi_env, _ object: napi_value, _ properties: [PropertyDescriptor]) throws { 23 | let props = try properties.map { try $0.value(env) } 24 | 25 | let status = props.withUnsafeBufferPointer { propertiesBytes in 26 | napi_define_properties(env, object, properties.count, propertiesBytes.baseAddress) 27 | } 28 | 29 | guard status == napi_ok else { throw NAPI.Error(status) } 30 | } 31 | 32 | public func initModule(_ env: napi_env, _ exports: napi_value, _ properties: [PropertyDescriptor]) -> napi_value { 33 | try! defineProperties(env, exports, properties) 34 | return exports 35 | } 36 | -------------------------------------------------------------------------------- /Sources/NAPI/Null.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | 3 | public struct Null: ValueConvertible { 4 | public static let `default` = Null() 5 | 6 | private init() {} 7 | 8 | public init(_ env: napi_env, from: napi_value) throws { 9 | guard try strictlyEquals(env, lhs: from, rhs: Null.default) else { 10 | napi_throw_type_error(env, nil, "Expected null") 11 | throw NAPI.Error.pendingException 12 | } 13 | } 14 | 15 | public func napiValue(_ env: napi_env) throws -> napi_value { 16 | var result: napi_value? 17 | 18 | let status = napi_get_null(env, &result) 19 | guard status == napi_ok else { throw NAPI.Error(status) } 20 | 21 | return result! 22 | } 23 | } 24 | 25 | extension Null: CustomStringConvertible { 26 | public var description: String { 27 | return "null" 28 | } 29 | } 30 | 31 | extension Null: Equatable { 32 | public static func ==(_: Null, _: Null) -> Bool { 33 | return true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/NAPI/PropertyDescriptor.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | import Foundation 3 | 4 | fileprivate enum InternalPropertyDescriptor { 5 | case method(String, Callback, napi_property_attributes) 6 | case value(String, ValueConvertible, napi_property_attributes) 7 | } 8 | 9 | public struct PropertyDescriptor { 10 | fileprivate let value: InternalPropertyDescriptor 11 | 12 | fileprivate init(_ value: InternalPropertyDescriptor) { 13 | self.value = value 14 | } 15 | 16 | func value(_ env: napi_env) throws -> napi_property_descriptor { 17 | switch self.value { 18 | case .method(let name, let callback, let attributes): 19 | let _name = try name.napiValue(env) 20 | let data = CallbackData(callback: callback) 21 | let dataPointer = Unmanaged.passRetained(data).toOpaque() 22 | return napi_property_descriptor(utf8name: nil, name: _name, method: swiftNAPICallback, getter: nil, setter: nil, value: nil, attributes: attributes, data: dataPointer) 23 | case .value(let name, let value, let attributes): 24 | let _name = try name.napiValue(env) 25 | let _value = try value.napiValue(env) 26 | return napi_property_descriptor(utf8name: nil, name: _name, method: nil, getter: nil, setter: nil, value: _value, attributes: attributes, data: nil) 27 | } 28 | } 29 | 30 | public static func value(_ name: String, _ value: ValueConvertible, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 31 | return .init(.value(name, value, attributes)) 32 | } 33 | 34 | public static func `class`(_ name: String, _ constructor: @escaping () throws -> This, _ properties: [PropertyDescriptor], attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 35 | return .init(.value(name, Class(named: name, { (env, args) in let native = try constructor(); try Wrap.wrap(env, jsObject: args.this, nativeObject: native); return nil }, properties), attributes)) 36 | } 37 | 38 | public static func `class`(_ name: String, _ constructor: @escaping (napi_env) throws -> This, _ properties: [PropertyDescriptor], attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 39 | return .init(.value(name, Class(named: name, { (env, args) in let native = try constructor(env); try Wrap.wrap(env, jsObject: args.this, nativeObject: native); return nil }, properties), attributes)) 40 | } 41 | 42 | /* (...) -> Void */ 43 | 44 | public static func function(_ name: String, _ callback: @escaping () throws -> Void, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 45 | return .init(.method(name, { (_, _) in try callback(); return Value.undefined }, attributes)) 46 | } 47 | 48 | public static func function(_ name: String, _ callback: @escaping (A) throws -> Void, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 49 | return .init(.method(name, { (env, args) in try callback(A(env, from: args.0)); return Value.undefined }, attributes)) 50 | } 51 | 52 | public static func function(_ name: String, _ callback: @escaping (A, B) throws -> Void, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 53 | return .init(.method(name, { (env, args) in try callback(A(env, from: args.0), B(env, from: args.1)); return Value.undefined }, attributes)) 54 | } 55 | 56 | /* (...) -> ValueConvertible */ 57 | 58 | public static func function(_ name: String, _ callback: @escaping () throws -> ValueConvertible, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 59 | return .init(.method(name, { (_, _) in return try callback() }, attributes)) 60 | } 61 | 62 | public static func function(_ name: String, _ callback: @escaping (A) throws -> ValueConvertible, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 63 | return .init(.method(name, { (env, args) in return try callback(A(env, from: args.0)) }, attributes)) 64 | } 65 | 66 | public static func function(_ name: String, _ callback: @escaping (A, B) throws -> ValueConvertible, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 67 | return .init(.method(name, { (env, args) in return try callback(A(env, from: args.0), B(env, from: args.1)) }, attributes)) 68 | } 69 | 70 | /* (this, ...) -> Void */ 71 | 72 | public static func method(_ name: String, _ callback: @escaping (This) throws -> Void, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 73 | return .init(.method(name, { (env, args) in try callback(Wrap.unwrap(env, jsObject: args.this)); return Value.undefined }, attributes)) 74 | } 75 | 76 | public static func method(_ name: String, _ callback: @escaping (This, A) throws -> Void, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 77 | return .init(.method(name, { (env, args) in try callback(Wrap.unwrap(env, jsObject: args.this), A(env, from: args.0)); return Value.undefined }, attributes)) 78 | } 79 | 80 | public static func method(_ name: String, _ callback: @escaping (This, A, B) throws -> Void, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 81 | return .init(.method(name, { (env, args) in try callback(Wrap.unwrap(env, jsObject: args.this), A(env, from: args.0), B(env, from: args.1)); return Value.undefined }, attributes)) 82 | } 83 | 84 | /* (this, ...) -> ValueConvertible */ 85 | 86 | public static func method(_ name: String, _ callback: @escaping (This) throws -> ValueConvertible, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 87 | return .init(.method(name, { (env, args) in return try callback(Wrap.unwrap(env, jsObject: args.this)) }, attributes)) 88 | } 89 | 90 | public static func method(_ name: String, _ callback: @escaping (This, A) throws -> ValueConvertible, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 91 | return .init(.method(name, { (env, args) in return try callback(Wrap.unwrap(env, jsObject: args.this), A(env, from: args.0)) }, attributes)) 92 | } 93 | 94 | public static func method(_ name: String, _ callback: @escaping (This, A, B) throws -> ValueConvertible, attributes: napi_property_attributes = napi_default) -> PropertyDescriptor { 95 | return .init(.method(name, { (env, args) in return try callback(Wrap.unwrap(env, jsObject: args.this), A(env, from: args.0), B(env, from: args.1)) }, attributes)) 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /Sources/NAPI/RunLoop.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import NAPIC 3 | 4 | fileprivate func setTimeout(_ env: napi_env, _ fn: Function, _ ms: Double) throws { 5 | var status: napi_status! 6 | 7 | var global: napi_value! 8 | status = napi_get_global(env, &global) 9 | guard status == napi_ok else { throw NAPI.Error(status) } 10 | 11 | var setTimeout: napi_value! 12 | status = napi_get_named_property(env, global, "setTimeout", &setTimeout) 13 | guard status == napi_ok else { throw NAPI.Error(status) } 14 | 15 | try Function(env, from: setTimeout).call(env, fn, ms) 16 | } 17 | 18 | public class RunLoop { 19 | private static var refCount = 0 20 | private static var scheduled = false 21 | 22 | private static func tick(_ env: napi_env) throws { 23 | guard RunLoop.refCount > 0 else { 24 | RunLoop.scheduled = false 25 | return 26 | } 27 | 28 | CFRunLoopRunInMode(.defaultMode, 0.02, false) 29 | try setTimeout(env, Function(named: "tick", RunLoop.tick), 0) 30 | } 31 | 32 | public static func ref(_ env: napi_env) throws { 33 | RunLoop.refCount += 1 34 | 35 | if RunLoop.scheduled == false { 36 | RunLoop.scheduled = true 37 | try setTimeout(env, Function(named: "tick", RunLoop.tick), 0) 38 | } 39 | } 40 | 41 | public static func unref() { 42 | RunLoop.refCount -= 1 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Sources/NAPI/Undefined.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | 3 | public struct Undefined: ValueConvertible { 4 | public static let `default` = Undefined() 5 | 6 | private init() {} 7 | 8 | public init(_ env: napi_env, from: napi_value) throws { 9 | guard try strictlyEquals(env, lhs: from, rhs: Undefined.default) else { 10 | napi_throw_type_error(env, nil, "Expected undefined") 11 | throw NAPI.Error.pendingException 12 | } 13 | } 14 | 15 | public func napiValue(_ env: napi_env) throws -> napi_value { 16 | var result: napi_value? 17 | 18 | let status = napi_get_undefined(env, &result) 19 | guard status == napi_ok else { throw NAPI.Error(status) } 20 | 21 | return result! 22 | } 23 | } 24 | 25 | extension Undefined: CustomStringConvertible { 26 | public var description: String { 27 | return "undefined" 28 | } 29 | } 30 | 31 | extension Undefined: Equatable { 32 | public static func ==(_: Undefined, _: Undefined) -> Bool { 33 | return true 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Sources/NAPI/Value.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | import Foundation 3 | 4 | public protocol ErrorConvertible: Swift.Error { 5 | var message: String { get } 6 | var code: String? { get } 7 | } 8 | 9 | fileprivate func throwError(_ env: napi_env, _ error: Swift.Error) throws { 10 | if let error = error as? NAPI.Error { 11 | let status = error.napi_throw(env) 12 | guard status == napi_ok else { throw NAPI.Error(status) } 13 | } else if let error = error as? ValueConvertible { 14 | let status = napi_throw(env, try error.napiValue(env)) 15 | guard status == napi_ok else { throw NAPI.Error(status) } 16 | } else if let error = error as? ErrorConvertible { 17 | let status = napi_throw_error(env, error.code, error.message) 18 | guard status == napi_ok else { throw NAPI.Error(status) } 19 | } else { 20 | let status = napi_throw_error(env, nil, error.localizedDescription) 21 | guard status == napi_ok else { throw NAPI.Error(status) } 22 | } 23 | } 24 | 25 | fileprivate func exceptionIsPending(_ env: napi_env) throws -> Bool { 26 | var result: Bool = false 27 | 28 | let status = napi_is_exception_pending(env, &result) 29 | guard status == napi_ok else { throw NAPI.Error(status) } 30 | 31 | return result 32 | } 33 | 34 | public typealias Callback = (napi_env, Arguments) throws -> ValueConvertible? 35 | 36 | class CallbackData { 37 | let callback: Callback 38 | 39 | init(callback: @escaping Callback) { 40 | self.callback = callback 41 | } 42 | } 43 | 44 | @_cdecl("swift_napi_callback") 45 | func swiftNAPICallback(_ env: napi_env!, _ cbinfo: napi_callback_info!) -> napi_value? { 46 | var args = NullableArguments(nil, nil, nil, nil, nil, nil, nil, nil, nil, nil, 10, nil) 47 | let dataPointer = UnsafeMutablePointer.allocate(capacity: 1) 48 | 49 | napi_get_cb_info(env, cbinfo, &args.length, &args.0, &args.this, dataPointer) 50 | let data = Unmanaged.fromOpaque(dataPointer.pointee!).takeUnretainedValue() 51 | 52 | do { 53 | return try data.callback(env, args as! Arguments)?.napiValue(env) 54 | } catch NAPI.Error.pendingException { 55 | return nil 56 | } catch { 57 | if try! exceptionIsPending(env) == false { try! throwError(env, error) } 58 | return nil 59 | } 60 | } 61 | 62 | public enum Value: ValueConvertible { 63 | case `class`(Class) 64 | case function(Function) 65 | case object([String: Value]) 66 | case array([Value]) 67 | case string(String) 68 | case number(Double) 69 | case boolean(Bool) 70 | case null 71 | case undefined 72 | 73 | public init(_ env: napi_env, from: napi_value) throws { 74 | fatalError("Not implemented") 75 | } 76 | 77 | public func napiValue(_ env: napi_env) throws -> napi_value { 78 | switch self { 79 | case .class(let `class`): return try `class`.napiValue(env) 80 | case .function(let function): return try function.napiValue(env) 81 | case .object(let object): return try object.napiValue(env) 82 | case .array(let array): return try array.napiValue(env) 83 | case .string(let string): return try string.napiValue(env) 84 | case .number(let number): return try number.napiValue(env) 85 | case .boolean(let boolean): return try boolean.napiValue(env) 86 | case .null: return try Null.default.napiValue(env) 87 | case .undefined: return try Undefined.default.napiValue(env) 88 | } 89 | } 90 | } 91 | 92 | extension Value: Decodable { 93 | public init(from decoder: Decoder) throws { 94 | let container = try decoder.singleValueContainer() 95 | 96 | if let object = try? container.decode([String: Value].self) { 97 | self = .object(object) 98 | } else if let array = try? container.decode([Value].self) { 99 | self = .array(array) 100 | } else if let string = try? container.decode(String.self) { 101 | self = .string(string) 102 | } else if let number = try? container.decode(Double.self) { 103 | self = .number(number) 104 | } else if let boolean = try? container.decode(Bool.self) { 105 | self = .boolean(boolean) 106 | } else if container.decodeNil() { 107 | self = .null 108 | } else { 109 | throw DecodingError.dataCorrupted(.init(codingPath: decoder.codingPath, debugDescription: "Failed to decode value")) 110 | } 111 | } 112 | } 113 | 114 | extension Value: CustomStringConvertible { 115 | public var description: String { 116 | switch self { 117 | case .class(_): return "[Function: ...]" 118 | case .function(_): return "[Function: ...]" 119 | case .object(let object): return "{ \(object.map({ "\($0): \($1)" }).joined(separator: ", "))) }" 120 | case .array(let array): return "[ \(array.map({ String(describing: $0) }).joined(separator: ", ")) ]" 121 | case .string(let string): return string 122 | case .number(let number): return String(describing: number) 123 | case .boolean(let boolean): return boolean ? "true" : "false" 124 | case .null: return "null" 125 | case .undefined: return "undefined" 126 | } 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /Sources/NAPI/ValueConvertible.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | import Foundation 3 | 4 | public protocol ValueConvertible { 5 | init(_ env: napi_env, from: napi_value) throws 6 | func napiValue(_ env: napi_env) throws -> napi_value 7 | } 8 | 9 | extension Optional: ValueConvertible where Wrapped: ValueConvertible { 10 | public init(_ env: napi_env, from: napi_value) throws { 11 | if try strictlyEquals(env, lhs: from, rhs: Null.default) { 12 | self = .none 13 | return 14 | } 15 | 16 | if try strictlyEquals(env, lhs: from, rhs: Undefined.default) { 17 | self = .none 18 | return 19 | } 20 | 21 | self = .some(try Wrapped.init(env, from: from)) 22 | } 23 | 24 | public func napiValue(_ env: napi_env) throws -> napi_value { 25 | return try self?.napiValue(env) ?? Value.null.napiValue(env) 26 | } 27 | } 28 | 29 | extension Dictionary: ValueConvertible where Key == String, Value: ValueConvertible { 30 | public init(_ env: napi_env, from: napi_value) throws { 31 | fatalError("Not implemented") 32 | } 33 | 34 | public func napiValue(_ env: napi_env) throws -> napi_value { 35 | var status: napi_status! 36 | var result: napi_value! 37 | 38 | status = napi_create_object(env, &result) 39 | guard status == napi_ok else { throw NAPI.Error(status) } 40 | 41 | for (key, value) in self { 42 | status = napi_set_property(env, result, try key.napiValue(env), try value.napiValue(env)) 43 | guard status == napi_ok else { throw NAPI.Error(status) } 44 | } 45 | 46 | return result 47 | } 48 | } 49 | 50 | extension Array: ValueConvertible where Element: ValueConvertible { 51 | public init(_ env: napi_env, from: napi_value) throws { 52 | fatalError("Not implemented") 53 | } 54 | 55 | public func napiValue(_ env: napi_env) throws -> napi_value { 56 | var status: napi_status! 57 | var result: napi_value! 58 | 59 | status = napi_create_array_with_length(env, self.count, &result) 60 | guard status == napi_ok else { throw NAPI.Error(status) } 61 | 62 | for (index, element) in self.enumerated() { 63 | status = napi_set_element(env, result, UInt32(index), try element.napiValue(env)) 64 | guard status == napi_ok else { throw NAPI.Error(status) } 65 | } 66 | 67 | return result 68 | } 69 | } 70 | 71 | extension String: ValueConvertible { 72 | public init(_ env: napi_env, from: napi_value) throws { 73 | var status: napi_status! 74 | var length: Int = 0 75 | 76 | status = napi_get_value_string_utf8(env, from, nil, 0, &length) 77 | guard status == napi_ok else { throw NAPI.Error(status) } 78 | 79 | var data = Data(count: length + 1) 80 | 81 | status = data.withUnsafeMutableBytes { 82 | napi_get_value_string_utf8(env, from, $0, length + 1, &length) 83 | } 84 | guard status == napi_ok else { throw NAPI.Error(status) } 85 | 86 | self.init(data: data.dropLast(), encoding: .utf8)! 87 | } 88 | 89 | public func napiValue(_ env: napi_env) throws -> napi_value { 90 | var result: napi_value? 91 | let data = self.data(using: .utf8)! 92 | 93 | let status = data.withUnsafeBytes { (bytes: UnsafePointer) in 94 | napi_create_string_utf8(env, bytes, data.count, &result) 95 | } 96 | 97 | guard status == napi_ok else { 98 | throw NAPI.Error(status) 99 | } 100 | 101 | return result! 102 | } 103 | } 104 | 105 | extension Double: ValueConvertible { 106 | public init(_ env: napi_env, from: napi_value) throws { 107 | self = .nan 108 | let status = napi_get_value_double(env, from, &self) 109 | guard status == napi_ok else { throw NAPI.Error(status) } 110 | } 111 | 112 | public func napiValue(_ env: napi_env) throws -> napi_value { 113 | var result: napi_value? 114 | let status = napi_create_double(env, self, &result) 115 | guard status == napi_ok else { throw NAPI.Error(status) } 116 | return result! 117 | } 118 | } 119 | 120 | extension Bool: ValueConvertible { 121 | public init(_ env: napi_env, from: napi_value) throws { 122 | self = false 123 | let status = napi_get_value_bool(env, from, &self) 124 | guard status == napi_ok else { throw NAPI.Error(status) } 125 | } 126 | 127 | public func napiValue(_ env: napi_env) throws -> napi_value { 128 | var result: napi_value? 129 | let status = napi_get_boolean(env, self, &result) 130 | guard status == napi_ok else { throw NAPI.Error(status) } 131 | return result! 132 | } 133 | } 134 | -------------------------------------------------------------------------------- /Sources/NAPI/Wrap.swift: -------------------------------------------------------------------------------- 1 | import NAPIC 2 | 3 | @_cdecl("swift_napi_deinit") 4 | func swiftNAPIDeinit(_ env: napi_env!, pointer: UnsafeMutableRawPointer?, hint: UnsafeMutableRawPointer?) { 5 | Unmanaged.fromOpaque(pointer!).release() 6 | } 7 | 8 | class Wrap { 9 | static func wrap(_ env: napi_env, jsObject: napi_value, nativeObject: T) throws { 10 | let pointer = Unmanaged.passRetained(nativeObject).toOpaque() 11 | let status = napi_wrap(env, jsObject, pointer, swiftNAPIDeinit, nil, nil) 12 | guard status == napi_ok else { throw NAPI.Error(status) } 13 | } 14 | 15 | static func unwrap(_ env: napi_env, jsObject: napi_value) throws -> T { 16 | var pointer: UnsafeMutableRawPointer? 17 | 18 | let status = napi_unwrap(env, jsObject, &pointer) 19 | guard status == napi_ok else { throw NAPI.Error(status) } 20 | 21 | return Unmanaged.fromOpaque(pointer!).takeUnretainedValue() 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Sources/NAPIC/include/NAPI.h: -------------------------------------------------------------------------------- 1 | #define BUILDING_NODE_EXTENSION 2 | 3 | #include "node_api.h" 4 | -------------------------------------------------------------------------------- /Sources/NAPIC/include/node_api.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_NODE_API_H_ 2 | #define SRC_NODE_API_H_ 3 | 4 | #include 5 | #include 6 | #include "node_api_types.h" 7 | 8 | struct uv_loop_s; // Forward declaration. 9 | 10 | #ifdef _WIN32 11 | #ifdef BUILDING_NODE_EXTENSION 12 | #ifdef EXTERNAL_NAPI 13 | // Building external N-API, or native module against external N-API 14 | #define NAPI_EXTERN /* nothing */ 15 | #else 16 | // Building native module against node with built-in N-API 17 | #define NAPI_EXTERN __declspec(dllimport) 18 | #endif 19 | #else 20 | // Building node with built-in N-API 21 | #define NAPI_EXTERN __declspec(dllexport) 22 | #endif 23 | #else 24 | #define NAPI_EXTERN /* nothing */ 25 | #endif 26 | 27 | #ifdef _WIN32 28 | # define NAPI_MODULE_EXPORT __declspec(dllexport) 29 | #else 30 | # define NAPI_MODULE_EXPORT __attribute__((visibility("default"))) 31 | #endif 32 | 33 | #ifdef __GNUC__ 34 | #define NAPI_NO_RETURN __attribute__((noreturn)) 35 | #else 36 | #define NAPI_NO_RETURN 37 | #endif 38 | 39 | 40 | typedef napi_value (*napi_addon_register_func)(napi_env env, 41 | napi_value exports); 42 | 43 | typedef struct { 44 | int nm_version; 45 | unsigned int nm_flags; 46 | const char* nm_filename; 47 | napi_addon_register_func nm_register_func; 48 | const char* nm_modname; 49 | void* nm_priv; 50 | void* reserved[4]; 51 | } napi_module; 52 | 53 | #define NAPI_MODULE_VERSION 1 54 | 55 | #if defined(_MSC_VER) 56 | #pragma section(".CRT$XCU", read) 57 | #define NAPI_C_CTOR(fn) \ 58 | static void __cdecl fn(void); \ 59 | __declspec(dllexport, allocate(".CRT$XCU")) void(__cdecl * fn##_)(void) = \ 60 | fn; \ 61 | static void __cdecl fn(void) 62 | #else 63 | #define NAPI_C_CTOR(fn) \ 64 | static void fn(void) __attribute__((constructor)); \ 65 | static void fn(void) 66 | #endif 67 | 68 | #ifdef __cplusplus 69 | #define EXTERN_C_START extern "C" { 70 | #define EXTERN_C_END } 71 | #else 72 | #define EXTERN_C_START 73 | #define EXTERN_C_END 74 | #endif 75 | 76 | #define NAPI_MODULE_X(modname, regfunc, priv, flags) \ 77 | EXTERN_C_START \ 78 | static napi_module _module = \ 79 | { \ 80 | NAPI_MODULE_VERSION, \ 81 | flags, \ 82 | __FILE__, \ 83 | regfunc, \ 84 | #modname, \ 85 | priv, \ 86 | {0}, \ 87 | }; \ 88 | NAPI_C_CTOR(_register_ ## modname) { \ 89 | napi_module_register(&_module); \ 90 | } \ 91 | EXTERN_C_END 92 | 93 | #define NAPI_MODULE(modname, regfunc) \ 94 | NAPI_MODULE_X(modname, regfunc, NULL, 0) // NOLINT (readability/null_usage) 95 | 96 | #define NAPI_AUTO_LENGTH SIZE_MAX 97 | 98 | EXTERN_C_START 99 | 100 | NAPI_EXTERN void napi_module_register(napi_module* mod); 101 | 102 | NAPI_EXTERN napi_status napi_add_env_cleanup_hook(napi_env env, 103 | void (*fun)(void* arg), 104 | void* arg); 105 | NAPI_EXTERN napi_status napi_remove_env_cleanup_hook(napi_env env, 106 | void (*fun)(void* arg), 107 | void* arg); 108 | 109 | NAPI_EXTERN napi_status 110 | napi_get_last_error_info(napi_env env, 111 | const napi_extended_error_info** result); 112 | 113 | NAPI_EXTERN napi_status napi_fatal_exception(napi_env env, napi_value err); 114 | 115 | NAPI_EXTERN NAPI_NO_RETURN void napi_fatal_error(const char* location, 116 | size_t location_len, 117 | const char* message, 118 | size_t message_len); 119 | 120 | // Getters for defined singletons 121 | NAPI_EXTERN napi_status napi_get_undefined(napi_env env, napi_value* result); 122 | NAPI_EXTERN napi_status napi_get_null(napi_env env, napi_value* result); 123 | NAPI_EXTERN napi_status napi_get_global(napi_env env, napi_value* result); 124 | NAPI_EXTERN napi_status napi_get_boolean(napi_env env, 125 | bool value, 126 | napi_value* result); 127 | 128 | // Methods to create Primitive types/Objects 129 | NAPI_EXTERN napi_status napi_create_object(napi_env env, napi_value* result); 130 | NAPI_EXTERN napi_status napi_create_array(napi_env env, napi_value* result); 131 | NAPI_EXTERN napi_status napi_create_array_with_length(napi_env env, 132 | size_t length, 133 | napi_value* result); 134 | NAPI_EXTERN napi_status napi_create_double(napi_env env, 135 | double value, 136 | napi_value* result); 137 | NAPI_EXTERN napi_status napi_create_int32(napi_env env, 138 | int32_t value, 139 | napi_value* result); 140 | NAPI_EXTERN napi_status napi_create_uint32(napi_env env, 141 | uint32_t value, 142 | napi_value* result); 143 | NAPI_EXTERN napi_status napi_create_int64(napi_env env, 144 | int64_t value, 145 | napi_value* result); 146 | NAPI_EXTERN napi_status napi_create_string_latin1(napi_env env, 147 | const char* str, 148 | size_t length, 149 | napi_value* result); 150 | NAPI_EXTERN napi_status napi_create_string_utf8(napi_env env, 151 | const char* str, 152 | size_t length, 153 | napi_value* result); 154 | NAPI_EXTERN napi_status napi_create_string_utf16(napi_env env, 155 | const char16_t* str, 156 | size_t length, 157 | napi_value* result); 158 | NAPI_EXTERN napi_status napi_create_symbol(napi_env env, 159 | napi_value description, 160 | napi_value* result); 161 | NAPI_EXTERN napi_status napi_create_function(napi_env env, 162 | const char* utf8name, 163 | size_t length, 164 | napi_callback cb, 165 | void* data, 166 | napi_value* result); 167 | NAPI_EXTERN napi_status napi_create_error(napi_env env, 168 | napi_value code, 169 | napi_value msg, 170 | napi_value* result); 171 | NAPI_EXTERN napi_status napi_create_type_error(napi_env env, 172 | napi_value code, 173 | napi_value msg, 174 | napi_value* result); 175 | NAPI_EXTERN napi_status napi_create_range_error(napi_env env, 176 | napi_value code, 177 | napi_value msg, 178 | napi_value* result); 179 | 180 | // Methods to get the native napi_value from Primitive type 181 | NAPI_EXTERN napi_status napi_typeof(napi_env env, 182 | napi_value value, 183 | napi_valuetype* result); 184 | NAPI_EXTERN napi_status napi_get_value_double(napi_env env, 185 | napi_value value, 186 | double* result); 187 | NAPI_EXTERN napi_status napi_get_value_int32(napi_env env, 188 | napi_value value, 189 | int32_t* result); 190 | NAPI_EXTERN napi_status napi_get_value_uint32(napi_env env, 191 | napi_value value, 192 | uint32_t* result); 193 | NAPI_EXTERN napi_status napi_get_value_int64(napi_env env, 194 | napi_value value, 195 | int64_t* result); 196 | NAPI_EXTERN napi_status napi_get_value_bool(napi_env env, 197 | napi_value value, 198 | bool* result); 199 | 200 | // Copies LATIN-1 encoded bytes from a string into a buffer. 201 | NAPI_EXTERN napi_status napi_get_value_string_latin1(napi_env env, 202 | napi_value value, 203 | char* buf, 204 | size_t bufsize, 205 | size_t* result); 206 | 207 | // Copies UTF-8 encoded bytes from a string into a buffer. 208 | NAPI_EXTERN napi_status napi_get_value_string_utf8(napi_env env, 209 | napi_value value, 210 | char* buf, 211 | size_t bufsize, 212 | size_t* result); 213 | 214 | // Copies UTF-16 encoded bytes from a string into a buffer. 215 | NAPI_EXTERN napi_status napi_get_value_string_utf16(napi_env env, 216 | napi_value value, 217 | char16_t* buf, 218 | size_t bufsize, 219 | size_t* result); 220 | 221 | // Methods to coerce values 222 | // These APIs may execute user scripts 223 | NAPI_EXTERN napi_status napi_coerce_to_bool(napi_env env, 224 | napi_value value, 225 | napi_value* result); 226 | NAPI_EXTERN napi_status napi_coerce_to_number(napi_env env, 227 | napi_value value, 228 | napi_value* result); 229 | NAPI_EXTERN napi_status napi_coerce_to_object(napi_env env, 230 | napi_value value, 231 | napi_value* result); 232 | NAPI_EXTERN napi_status napi_coerce_to_string(napi_env env, 233 | napi_value value, 234 | napi_value* result); 235 | 236 | // Methods to work with Objects 237 | NAPI_EXTERN napi_status napi_get_prototype(napi_env env, 238 | napi_value object, 239 | napi_value* result); 240 | NAPI_EXTERN napi_status napi_get_property_names(napi_env env, 241 | napi_value object, 242 | napi_value* result); 243 | NAPI_EXTERN napi_status napi_set_property(napi_env env, 244 | napi_value object, 245 | napi_value key, 246 | napi_value value); 247 | NAPI_EXTERN napi_status napi_has_property(napi_env env, 248 | napi_value object, 249 | napi_value key, 250 | bool* result); 251 | NAPI_EXTERN napi_status napi_get_property(napi_env env, 252 | napi_value object, 253 | napi_value key, 254 | napi_value* result); 255 | NAPI_EXTERN napi_status napi_delete_property(napi_env env, 256 | napi_value object, 257 | napi_value key, 258 | bool* result); 259 | NAPI_EXTERN napi_status napi_has_own_property(napi_env env, 260 | napi_value object, 261 | napi_value key, 262 | bool* result); 263 | NAPI_EXTERN napi_status napi_set_named_property(napi_env env, 264 | napi_value object, 265 | const char* utf8name, 266 | napi_value value); 267 | NAPI_EXTERN napi_status napi_has_named_property(napi_env env, 268 | napi_value object, 269 | const char* utf8name, 270 | bool* result); 271 | NAPI_EXTERN napi_status napi_get_named_property(napi_env env, 272 | napi_value object, 273 | const char* utf8name, 274 | napi_value* result); 275 | NAPI_EXTERN napi_status napi_set_element(napi_env env, 276 | napi_value object, 277 | uint32_t index, 278 | napi_value value); 279 | NAPI_EXTERN napi_status napi_has_element(napi_env env, 280 | napi_value object, 281 | uint32_t index, 282 | bool* result); 283 | NAPI_EXTERN napi_status napi_get_element(napi_env env, 284 | napi_value object, 285 | uint32_t index, 286 | napi_value* result); 287 | NAPI_EXTERN napi_status napi_delete_element(napi_env env, 288 | napi_value object, 289 | uint32_t index, 290 | bool* result); 291 | NAPI_EXTERN napi_status 292 | napi_define_properties(napi_env env, 293 | napi_value object, 294 | size_t property_count, 295 | const napi_property_descriptor* properties); 296 | 297 | // Methods to work with Arrays 298 | NAPI_EXTERN napi_status napi_is_array(napi_env env, 299 | napi_value value, 300 | bool* result); 301 | NAPI_EXTERN napi_status napi_get_array_length(napi_env env, 302 | napi_value value, 303 | uint32_t* result); 304 | 305 | // Methods to compare values 306 | NAPI_EXTERN napi_status napi_strict_equals(napi_env env, 307 | napi_value lhs, 308 | napi_value rhs, 309 | bool* result); 310 | 311 | // Methods to work with Functions 312 | NAPI_EXTERN napi_status napi_call_function(napi_env env, 313 | napi_value recv, 314 | napi_value func, 315 | size_t argc, 316 | const napi_value* argv, 317 | napi_value* result); 318 | NAPI_EXTERN napi_status napi_new_instance(napi_env env, 319 | napi_value constructor, 320 | size_t argc, 321 | const napi_value* argv, 322 | napi_value* result); 323 | NAPI_EXTERN napi_status napi_instanceof(napi_env env, 324 | napi_value object, 325 | napi_value constructor, 326 | bool* result); 327 | 328 | // Methods to work with napi_callbacks 329 | 330 | // Gets all callback info in a single call. (Ugly, but faster.) 331 | NAPI_EXTERN napi_status napi_get_cb_info( 332 | napi_env env, // [in] NAPI environment handle 333 | napi_callback_info cbinfo, // [in] Opaque callback-info handle 334 | size_t* argc, // [in-out] Specifies the size of the provided argv array 335 | // and receives the actual count of args. 336 | napi_value* argv, // [out] Array of values 337 | napi_value* this_arg, // [out] Receives the JS 'this' arg for the call 338 | void** data); // [out] Receives the data pointer for the callback. 339 | 340 | NAPI_EXTERN napi_status napi_get_new_target(napi_env env, 341 | napi_callback_info cbinfo, 342 | napi_value* result); 343 | NAPI_EXTERN napi_status 344 | napi_define_class(napi_env env, 345 | const char* utf8name, 346 | size_t length, 347 | napi_callback constructor, 348 | void* data, 349 | size_t property_count, 350 | const napi_property_descriptor* properties, 351 | napi_value* result); 352 | 353 | // Methods to work with external data objects 354 | NAPI_EXTERN napi_status napi_wrap(napi_env env, 355 | napi_value js_object, 356 | void* native_object, 357 | napi_finalize finalize_cb, 358 | void* finalize_hint, 359 | napi_ref* result); 360 | NAPI_EXTERN napi_status napi_unwrap(napi_env env, 361 | napi_value js_object, 362 | void** result); 363 | NAPI_EXTERN napi_status napi_remove_wrap(napi_env env, 364 | napi_value js_object, 365 | void** result); 366 | NAPI_EXTERN napi_status napi_create_external(napi_env env, 367 | void* data, 368 | napi_finalize finalize_cb, 369 | void* finalize_hint, 370 | napi_value* result); 371 | NAPI_EXTERN napi_status napi_get_value_external(napi_env env, 372 | napi_value value, 373 | void** result); 374 | 375 | // Methods to control object lifespan 376 | 377 | // Set initial_refcount to 0 for a weak reference, >0 for a strong reference. 378 | NAPI_EXTERN napi_status napi_create_reference(napi_env env, 379 | napi_value value, 380 | uint32_t initial_refcount, 381 | napi_ref* result); 382 | 383 | // Deletes a reference. The referenced value is released, and may 384 | // be GC'd unless there are other references to it. 385 | NAPI_EXTERN napi_status napi_delete_reference(napi_env env, napi_ref ref); 386 | 387 | // Increments the reference count, optionally returning the resulting count. 388 | // After this call the reference will be a strong reference because its 389 | // refcount is >0, and the referenced object is effectively "pinned". 390 | // Calling this when the refcount is 0 and the object is unavailable 391 | // results in an error. 392 | NAPI_EXTERN napi_status napi_reference_ref(napi_env env, 393 | napi_ref ref, 394 | uint32_t* result); 395 | 396 | // Decrements the reference count, optionally returning the resulting count. 397 | // If the result is 0 the reference is now weak and the object may be GC'd 398 | // at any time if there are no other references. Calling this when the 399 | // refcount is already 0 results in an error. 400 | NAPI_EXTERN napi_status napi_reference_unref(napi_env env, 401 | napi_ref ref, 402 | uint32_t* result); 403 | 404 | // Attempts to get a referenced value. If the reference is weak, 405 | // the value might no longer be available, in that case the call 406 | // is still successful but the result is NULL. 407 | NAPI_EXTERN napi_status napi_get_reference_value(napi_env env, 408 | napi_ref ref, 409 | napi_value* result); 410 | 411 | NAPI_EXTERN napi_status napi_open_handle_scope(napi_env env, 412 | napi_handle_scope* result); 413 | NAPI_EXTERN napi_status napi_close_handle_scope(napi_env env, 414 | napi_handle_scope scope); 415 | NAPI_EXTERN napi_status 416 | napi_open_escapable_handle_scope(napi_env env, 417 | napi_escapable_handle_scope* result); 418 | NAPI_EXTERN napi_status 419 | napi_close_escapable_handle_scope(napi_env env, 420 | napi_escapable_handle_scope scope); 421 | 422 | NAPI_EXTERN napi_status napi_escape_handle(napi_env env, 423 | napi_escapable_handle_scope scope, 424 | napi_value escapee, 425 | napi_value* result); 426 | 427 | NAPI_EXTERN napi_status napi_open_callback_scope(napi_env env, 428 | napi_value resource_object, 429 | napi_async_context context, 430 | napi_callback_scope* result); 431 | 432 | NAPI_EXTERN napi_status napi_close_callback_scope(napi_env env, 433 | napi_callback_scope scope); 434 | 435 | // Methods to support error handling 436 | NAPI_EXTERN napi_status napi_throw(napi_env env, napi_value error); 437 | NAPI_EXTERN napi_status napi_throw_error(napi_env env, 438 | const char* code, 439 | const char* msg); 440 | NAPI_EXTERN napi_status napi_throw_type_error(napi_env env, 441 | const char* code, 442 | const char* msg); 443 | NAPI_EXTERN napi_status napi_throw_range_error(napi_env env, 444 | const char* code, 445 | const char* msg); 446 | NAPI_EXTERN napi_status napi_is_error(napi_env env, 447 | napi_value value, 448 | bool* result); 449 | 450 | // Methods to support catching exceptions 451 | NAPI_EXTERN napi_status napi_is_exception_pending(napi_env env, bool* result); 452 | NAPI_EXTERN napi_status napi_get_and_clear_last_exception(napi_env env, 453 | napi_value* result); 454 | 455 | // Methods to provide node::Buffer functionality with napi types 456 | NAPI_EXTERN napi_status napi_create_buffer(napi_env env, 457 | size_t length, 458 | void** data, 459 | napi_value* result); 460 | NAPI_EXTERN napi_status napi_create_external_buffer(napi_env env, 461 | size_t length, 462 | void* data, 463 | napi_finalize finalize_cb, 464 | void* finalize_hint, 465 | napi_value* result); 466 | NAPI_EXTERN napi_status napi_create_buffer_copy(napi_env env, 467 | size_t length, 468 | const void* data, 469 | void** result_data, 470 | napi_value* result); 471 | NAPI_EXTERN napi_status napi_is_buffer(napi_env env, 472 | napi_value value, 473 | bool* result); 474 | NAPI_EXTERN napi_status napi_get_buffer_info(napi_env env, 475 | napi_value value, 476 | void** data, 477 | size_t* length); 478 | 479 | // Methods to work with array buffers and typed arrays 480 | NAPI_EXTERN napi_status napi_is_arraybuffer(napi_env env, 481 | napi_value value, 482 | bool* result); 483 | NAPI_EXTERN napi_status napi_create_arraybuffer(napi_env env, 484 | size_t byte_length, 485 | void** data, 486 | napi_value* result); 487 | NAPI_EXTERN napi_status 488 | napi_create_external_arraybuffer(napi_env env, 489 | void* external_data, 490 | size_t byte_length, 491 | napi_finalize finalize_cb, 492 | void* finalize_hint, 493 | napi_value* result); 494 | NAPI_EXTERN napi_status napi_get_arraybuffer_info(napi_env env, 495 | napi_value arraybuffer, 496 | void** data, 497 | size_t* byte_length); 498 | NAPI_EXTERN napi_status napi_is_typedarray(napi_env env, 499 | napi_value value, 500 | bool* result); 501 | NAPI_EXTERN napi_status napi_create_typedarray(napi_env env, 502 | napi_typedarray_type type, 503 | size_t length, 504 | napi_value arraybuffer, 505 | size_t byte_offset, 506 | napi_value* result); 507 | NAPI_EXTERN napi_status napi_get_typedarray_info(napi_env env, 508 | napi_value typedarray, 509 | napi_typedarray_type* type, 510 | size_t* length, 511 | void** data, 512 | napi_value* arraybuffer, 513 | size_t* byte_offset); 514 | 515 | NAPI_EXTERN napi_status napi_create_dataview(napi_env env, 516 | size_t length, 517 | napi_value arraybuffer, 518 | size_t byte_offset, 519 | napi_value* result); 520 | NAPI_EXTERN napi_status napi_is_dataview(napi_env env, 521 | napi_value value, 522 | bool* result); 523 | NAPI_EXTERN napi_status napi_get_dataview_info(napi_env env, 524 | napi_value dataview, 525 | size_t* bytelength, 526 | void** data, 527 | napi_value* arraybuffer, 528 | size_t* byte_offset); 529 | 530 | // Methods to manage simple async operations 531 | NAPI_EXTERN 532 | napi_status napi_create_async_work(napi_env env, 533 | napi_value async_resource, 534 | napi_value async_resource_name, 535 | napi_async_execute_callback execute, 536 | napi_async_complete_callback complete, 537 | void* data, 538 | napi_async_work* result); 539 | NAPI_EXTERN napi_status napi_delete_async_work(napi_env env, 540 | napi_async_work work); 541 | NAPI_EXTERN napi_status napi_queue_async_work(napi_env env, 542 | napi_async_work work); 543 | NAPI_EXTERN napi_status napi_cancel_async_work(napi_env env, 544 | napi_async_work work); 545 | 546 | // Methods for custom handling of async operations 547 | NAPI_EXTERN napi_status napi_async_init(napi_env env, 548 | napi_value async_resource, 549 | napi_value async_resource_name, 550 | napi_async_context* result); 551 | 552 | NAPI_EXTERN napi_status napi_async_destroy(napi_env env, 553 | napi_async_context async_context); 554 | 555 | NAPI_EXTERN napi_status napi_make_callback(napi_env env, 556 | napi_async_context async_context, 557 | napi_value recv, 558 | napi_value func, 559 | size_t argc, 560 | const napi_value* argv, 561 | napi_value* result); 562 | 563 | // version management 564 | NAPI_EXTERN napi_status napi_get_version(napi_env env, uint32_t* result); 565 | 566 | NAPI_EXTERN 567 | napi_status napi_get_node_version(napi_env env, 568 | const napi_node_version** version); 569 | 570 | // Promises 571 | NAPI_EXTERN napi_status napi_create_promise(napi_env env, 572 | napi_deferred* deferred, 573 | napi_value* promise); 574 | NAPI_EXTERN napi_status napi_resolve_deferred(napi_env env, 575 | napi_deferred deferred, 576 | napi_value resolution); 577 | NAPI_EXTERN napi_status napi_reject_deferred(napi_env env, 578 | napi_deferred deferred, 579 | napi_value rejection); 580 | NAPI_EXTERN napi_status napi_is_promise(napi_env env, 581 | napi_value promise, 582 | bool* is_promise); 583 | 584 | // Memory management 585 | NAPI_EXTERN napi_status napi_adjust_external_memory(napi_env env, 586 | int64_t change_in_bytes, 587 | int64_t* adjusted_value); 588 | 589 | // Runnig a script 590 | NAPI_EXTERN napi_status napi_run_script(napi_env env, 591 | napi_value script, 592 | napi_value* result); 593 | 594 | // Return the current libuv event loop for a given environment 595 | NAPI_EXTERN napi_status napi_get_uv_event_loop(napi_env env, 596 | struct uv_loop_s** loop); 597 | 598 | EXTERN_C_END 599 | 600 | #endif // SRC_NODE_API_H_ 601 | -------------------------------------------------------------------------------- /Sources/NAPIC/include/node_api_types.h: -------------------------------------------------------------------------------- 1 | #ifndef SRC_NODE_API_TYPES_H_ 2 | #define SRC_NODE_API_TYPES_H_ 3 | 4 | #include 5 | #include 6 | 7 | #if !defined __cplusplus || (defined(_MSC_VER) && _MSC_VER < 1900) 8 | typedef uint16_t char16_t; 9 | #endif 10 | 11 | // JSVM API types are all opaque pointers for ABI stability 12 | // typedef undefined structs instead of void* for compile time type safety 13 | typedef struct napi_env__ *napi_env; 14 | typedef struct napi_value__ *napi_value; 15 | typedef struct napi_ref__ *napi_ref; 16 | typedef struct napi_handle_scope__ *napi_handle_scope; 17 | typedef struct napi_escapable_handle_scope__ *napi_escapable_handle_scope; 18 | typedef struct napi_callback_scope__ *napi_callback_scope; 19 | typedef struct napi_callback_info__ *napi_callback_info; 20 | typedef struct napi_async_context__ *napi_async_context; 21 | typedef struct napi_async_work__ *napi_async_work; 22 | typedef struct napi_deferred__ *napi_deferred; 23 | 24 | typedef enum { 25 | napi_default = 0, 26 | napi_writable = 1 << 0, 27 | napi_enumerable = 1 << 1, 28 | napi_configurable = 1 << 2, 29 | 30 | // Used with napi_define_class to distinguish static properties 31 | // from instance properties. Ignored by napi_define_properties. 32 | napi_static = 1 << 10, 33 | } napi_property_attributes; 34 | 35 | typedef enum { 36 | // ES6 types (corresponds to typeof) 37 | napi_undefined, 38 | napi_null, 39 | napi_boolean, 40 | napi_number, 41 | napi_string, 42 | napi_symbol, 43 | napi_object, 44 | napi_function, 45 | napi_external, 46 | } napi_valuetype; 47 | 48 | typedef enum { 49 | napi_int8_array, 50 | napi_uint8_array, 51 | napi_uint8_clamped_array, 52 | napi_int16_array, 53 | napi_uint16_array, 54 | napi_int32_array, 55 | napi_uint32_array, 56 | napi_float32_array, 57 | napi_float64_array, 58 | } napi_typedarray_type; 59 | 60 | typedef enum { 61 | napi_ok, 62 | napi_invalid_arg, 63 | napi_object_expected, 64 | napi_string_expected, 65 | napi_name_expected, 66 | napi_function_expected, 67 | napi_number_expected, 68 | napi_boolean_expected, 69 | napi_array_expected, 70 | napi_generic_failure, 71 | napi_pending_exception, 72 | napi_cancelled, 73 | napi_escape_called_twice, 74 | napi_handle_scope_mismatch, 75 | napi_callback_scope_mismatch 76 | } napi_status; 77 | 78 | typedef napi_value (*napi_callback)(napi_env env, 79 | napi_callback_info info); 80 | typedef void (*napi_finalize)(napi_env env, 81 | void* finalize_data, 82 | void* finalize_hint); 83 | typedef void (*napi_async_execute_callback)(napi_env env, 84 | void* data); 85 | typedef void (*napi_async_complete_callback)(napi_env env, 86 | napi_status status, 87 | void* data); 88 | 89 | typedef struct { 90 | // One of utf8name or name should be NULL. 91 | const char* utf8name; 92 | napi_value name; 93 | 94 | napi_callback method; 95 | napi_callback getter; 96 | napi_callback setter; 97 | napi_value value; 98 | 99 | napi_property_attributes attributes; 100 | void* data; 101 | } napi_property_descriptor; 102 | 103 | typedef struct { 104 | const char* error_message; 105 | void* engine_reserved; 106 | uint32_t engine_error_code; 107 | napi_status error_code; 108 | } napi_extended_error_info; 109 | 110 | typedef struct { 111 | uint32_t major; 112 | uint32_t minor; 113 | uint32_t patch; 114 | const char* release; 115 | } napi_node_version; 116 | 117 | #endif // SRC_NODE_API_TYPES_H_ 118 | -------------------------------------------------------------------------------- /Sources/NAPIC/source.c: -------------------------------------------------------------------------------- 1 | // This file is empty since the implementation of N-API is supplied by Node.js at runtime 2 | -------------------------------------------------------------------------------- /Tests/.gitignore: -------------------------------------------------------------------------------- 1 | /.build/ 2 | /node_modules/ 3 | /package-lock.json 4 | /Package.resolved 5 | -------------------------------------------------------------------------------- /Tests/Package.swift: -------------------------------------------------------------------------------- 1 | // swift-tools-version:4.2 2 | 3 | import PackageDescription 4 | 5 | let package = Package( 6 | name: "NAPITests", 7 | products: [ 8 | .library(name: "NAPITests", type: .dynamic, targets: ["NAPITests"]), 9 | ], 10 | dependencies: [ 11 | .package(path: "../"), 12 | ], 13 | targets: [ 14 | .target(name: "Trampoline", dependencies: ["NAPIC"]), 15 | .target(name: "NAPITests", dependencies: ["NAPI", "Trampoline"]), 16 | ] 17 | ) 18 | -------------------------------------------------------------------------------- /Tests/Sources/NAPITests/NAPITests.swift: -------------------------------------------------------------------------------- 1 | import NAPI 2 | 3 | struct TestError: Swift.Error, ErrorConvertible { 4 | public var message: String 5 | public var code: String? 6 | } 7 | 8 | func assertEqual(expected: T, actual: T) throws { 9 | guard expected == actual else { 10 | throw TestError(message: "Expected values to be equal:\n\n\(expected) !== \(actual)\n", code: "ERR_ASSERTION") 11 | } 12 | } 13 | 14 | func returnString() -> String { 15 | return "a string" 16 | } 17 | 18 | func returnNumber() -> Double { 19 | return 1337 20 | } 21 | 22 | func returnBoolean() -> Bool { 23 | return true 24 | } 25 | 26 | func returnNull() -> Value { 27 | return .null 28 | } 29 | 30 | func returnUndefined() -> Value { 31 | return .undefined 32 | } 33 | 34 | func takeString(value: String) throws { 35 | try assertEqual(expected: "a string", actual: value) 36 | } 37 | 38 | func takeNumber(value: Double) throws { 39 | try assertEqual(expected: 1337, actual: value) 40 | } 41 | 42 | func takeBoolean(value: Bool) throws { 43 | try assertEqual(expected: true, actual: value) 44 | } 45 | 46 | func takeNull(value: Null) throws { 47 | try assertEqual(expected: Null.default, actual: value) 48 | } 49 | 50 | func takeUndefined(value: Undefined) throws { 51 | try assertEqual(expected: Undefined.default, actual: value) 52 | } 53 | 54 | func takeOptionalString(value: String?) -> String { 55 | return value ?? "a string" 56 | } 57 | 58 | func takeOptionalNumber(value: Double?) -> Double { 59 | return value ?? 1337 60 | } 61 | 62 | func takeOptionalBoolean(value: Bool?) -> Bool { 63 | return value ?? true 64 | } 65 | 66 | func throwError() throws { 67 | throw TestError(message: "Error message", code: "ETEST") 68 | } 69 | 70 | @_cdecl("_init_napi_tests") 71 | func initNAPITests(env: OpaquePointer, exports: OpaquePointer) -> OpaquePointer? { 72 | return initModule(env, exports, [ 73 | .function("returnString", returnString), 74 | .function("returnNumber", returnNumber), 75 | .function("returnBoolean", returnBoolean), 76 | .function("returnNull", returnNull), 77 | .function("returnUndefined", returnUndefined), 78 | 79 | .function("takeString", takeString), 80 | .function("takeNumber", takeNumber), 81 | .function("takeBoolean", takeBoolean), 82 | .function("takeNull", takeNull), 83 | .function("takeUndefined", takeUndefined), 84 | 85 | .function("takeOptionalString", takeOptionalString), 86 | .function("takeOptionalNumber", takeOptionalNumber), 87 | .function("takeOptionalBoolean", takeOptionalBoolean), 88 | 89 | .function("throwError", throwError), 90 | ]) 91 | } 92 | -------------------------------------------------------------------------------- /Tests/Sources/Trampoline/include/Trampoline.h: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/LinusU/swift-napi-bindings/c763cebc49a45a91a2bc092a8057a2c7d5846b96/Tests/Sources/Trampoline/include/Trampoline.h -------------------------------------------------------------------------------- /Tests/Sources/Trampoline/trampoline.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | napi_value _init_napi_tests(napi_env, napi_value); 4 | 5 | NAPI_MODULE(napi_tests, _init_napi_tests) 6 | -------------------------------------------------------------------------------- /Tests/index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./.build/release/NAPITests.node') 2 | -------------------------------------------------------------------------------- /Tests/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "napi-tests", 3 | "version": "0.0.0", 4 | "private": true, 5 | "scripts": { 6 | "test": "standard && mocha", 7 | "postinstall": "swift build -c release -Xlinker -undefined -Xlinker dynamic_lookup && mv .build/release/libNAPITests.dylib .build/release/NAPITests.node" 8 | }, 9 | "devDependencies": { 10 | "mocha": "^6.0.0", 11 | "standard": "^12.0.1" 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /Tests/test/errors.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const assert = require('assert') 4 | const addon = require('../') 5 | 6 | describe('Errors', () => { 7 | it('throws an error', () => { 8 | assert.throws(() => addon.throwError(), (err) => { 9 | return (err.message === 'Error message' && err.code === 'ETEST') 10 | }) 11 | }) 12 | }) 13 | -------------------------------------------------------------------------------- /Tests/test/optional-values.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const addon = require('../') 4 | const assert = require('assert') 5 | 6 | describe('Optional Values', () => { 7 | it('takes optional strings', () => { 8 | assert.strictEqual(addon.takeOptionalString(), 'a string') 9 | assert.strictEqual(addon.takeOptionalString(null), 'a string') 10 | assert.strictEqual(addon.takeOptionalString(undefined), 'a string') 11 | assert.strictEqual(addon.takeOptionalString('a string'), 'a string') 12 | }) 13 | 14 | it('takes optional numbers', () => { 15 | assert.strictEqual(addon.takeOptionalNumber(), 1337) 16 | assert.strictEqual(addon.takeOptionalNumber(null), 1337) 17 | assert.strictEqual(addon.takeOptionalNumber(undefined), 1337) 18 | assert.strictEqual(addon.takeOptionalNumber(1337), 1337) 19 | }) 20 | 21 | it('takes optional booleans', () => { 22 | assert.strictEqual(addon.takeOptionalBoolean(), true) 23 | assert.strictEqual(addon.takeOptionalBoolean(null), true) 24 | assert.strictEqual(addon.takeOptionalBoolean(undefined), true) 25 | assert.strictEqual(addon.takeOptionalBoolean(true), true) 26 | }) 27 | }) 28 | -------------------------------------------------------------------------------- /Tests/test/return-values.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const assert = require('assert') 4 | const addon = require('../') 5 | 6 | describe('Return Values', () => { 7 | it('returns strings', () => { 8 | assert.strictEqual(addon.returnString(), 'a string') 9 | }) 10 | 11 | it('returns numbers', () => { 12 | assert.strictEqual(addon.returnNumber(), 1337) 13 | }) 14 | 15 | it('returns bolleans', () => { 16 | assert.strictEqual(addon.returnBoolean(), true) 17 | }) 18 | 19 | it('returns null', () => { 20 | assert.strictEqual(addon.returnNull(), null) 21 | }) 22 | 23 | it('returns undefined', () => { 24 | assert.strictEqual(addon.returnUndefined(), undefined) 25 | }) 26 | }) 27 | -------------------------------------------------------------------------------- /Tests/test/take-values.js: -------------------------------------------------------------------------------- 1 | /* eslint-env mocha */ 2 | 3 | const addon = require('../') 4 | const assert = require('assert') 5 | 6 | describe('Take Values', () => { 7 | it('takes strings', () => { 8 | addon.takeString('a string') 9 | }) 10 | 11 | it('takes numbers', () => { 12 | addon.takeNumber(1337) 13 | }) 14 | 15 | it('takes booleans', () => { 16 | addon.takeBoolean(true) 17 | }) 18 | 19 | it('takes null', () => { 20 | addon.takeNull(null) 21 | }) 22 | 23 | it('takes undefined', () => { 24 | addon.takeUndefined(undefined) 25 | }) 26 | 27 | describe('Errors', () => { 28 | it('expects strings', () => { 29 | assert.throws(() => addon.takeString(1337), (err) => err instanceof TypeError && err.message === 'Expected string') 30 | assert.throws(() => addon.takeString(true), (err) => err instanceof TypeError && err.message === 'Expected string') 31 | assert.throws(() => addon.takeString(null), (err) => err instanceof TypeError && err.message === 'Expected string') 32 | assert.throws(() => addon.takeString(undefined), (err) => err instanceof TypeError && err.message === 'Expected string') 33 | assert.throws(() => addon.takeString(), (err) => err instanceof TypeError && err.message === 'Expected string') 34 | }) 35 | 36 | it('expects numbers', () => { 37 | assert.throws(() => addon.takeNumber('a string'), (err) => err instanceof TypeError && err.message === 'Expected number') 38 | assert.throws(() => addon.takeNumber(true), (err) => err instanceof TypeError && err.message === 'Expected number') 39 | assert.throws(() => addon.takeNumber(null), (err) => err instanceof TypeError && err.message === 'Expected number') 40 | assert.throws(() => addon.takeNumber(undefined), (err) => err instanceof TypeError && err.message === 'Expected number') 41 | assert.throws(() => addon.takeNumber(), (err) => err instanceof TypeError && err.message === 'Expected number') 42 | }) 43 | 44 | it('expects booleans', () => { 45 | assert.throws(() => addon.takeBoolean('a string'), (err) => err instanceof TypeError && err.message === 'Expected boolean') 46 | assert.throws(() => addon.takeBoolean(1337), (err) => err instanceof TypeError && err.message === 'Expected boolean') 47 | assert.throws(() => addon.takeBoolean(null), (err) => err instanceof TypeError && err.message === 'Expected boolean') 48 | assert.throws(() => addon.takeBoolean(undefined), (err) => err instanceof TypeError && err.message === 'Expected boolean') 49 | assert.throws(() => addon.takeBoolean(), (err) => err instanceof TypeError && err.message === 'Expected boolean') 50 | }) 51 | 52 | it('expects null', () => { 53 | assert.throws(() => addon.takeNull('a string'), (err) => err instanceof TypeError && err.message === 'Expected null') 54 | assert.throws(() => addon.takeNull(1337), (err) => err instanceof TypeError && err.message === 'Expected null') 55 | assert.throws(() => addon.takeNull(true), (err) => err instanceof TypeError && err.message === 'Expected null') 56 | assert.throws(() => addon.takeNull(undefined), (err) => err instanceof TypeError && err.message === 'Expected null') 57 | assert.throws(() => addon.takeNull(), (err) => err instanceof TypeError && err.message === 'Expected null') 58 | }) 59 | 60 | it('expects undefined', () => { 61 | assert.throws(() => addon.takeUndefined('a string'), (err) => err instanceof TypeError && err.message === 'Expected undefined') 62 | assert.throws(() => addon.takeUndefined(1337), (err) => err instanceof TypeError && err.message === 'Expected undefined') 63 | assert.throws(() => addon.takeUndefined(true), (err) => err instanceof TypeError && err.message === 'Expected undefined') 64 | assert.throws(() => addon.takeUndefined(null), (err) => err instanceof TypeError && err.message === 'Expected undefined') 65 | }) 66 | }) 67 | }) 68 | -------------------------------------------------------------------------------- /readme.md: -------------------------------------------------------------------------------- 1 | # N-API Swift Bindings 2 | 3 | This package provides bindings to the [Node.js N-API](https://nodejs.org/api/n-api.html) so that you can build Node.js Addons in Swift. 4 | 5 | **Note:** This is very much a work in progress, expect the API to change drastically between versions! I'll be adding documentation for how to get up and running, and building addons soon™ 6 | 7 | See some simple examples here: [LinusU/swift-node-addon-examples](https://github.com/LinusU/swift-node-addon-examples) 8 | --------------------------------------------------------------------------------