diff --git a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift index 6679c4a8..2a10a360 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift @@ -152,6 +152,240 @@ public class ExportSwift { ) } + /// Detects whether given expression is supported as default parameter value + private func isSupportedDefaultValueExpression(_ initClause: InitializerClauseSyntax) -> Bool { + let expression = initClause.value + + // Function calls are checked later in extractDefaultValue (as constructors are allowed) + if expression.is(ArrayExprSyntax.self) { return false } + if expression.is(DictionaryExprSyntax.self) { return false } + if expression.is(BinaryOperatorExprSyntax.self) { return false } + if expression.is(ClosureExprSyntax.self) { return false } + + // Method call chains (e.g., obj.foo()) + if let memberExpression = expression.as(MemberAccessExprSyntax.self), + memberExpression.base?.is(FunctionCallExprSyntax.self) == true + { + return false + } + + return true + } + + /// Extract enum case value from member access expression + private func extractEnumCaseValue( + from memberExpr: MemberAccessExprSyntax, + type: BridgeType + ) -> DefaultValue? { + let caseName = memberExpr.declName.baseName.text + + let enumName: String? + switch type { + case .caseEnum(let name), .rawValueEnum(let name, _), .associatedValueEnum(let name): + enumName = name + case .optional(let wrappedType): + switch wrappedType { + case .caseEnum(let name), .rawValueEnum(let name, _), .associatedValueEnum(let name): + enumName = name + default: + return nil + } + default: + return nil + } + + guard let enumName = enumName else { return nil } + + if memberExpr.base == nil { + return .enumCase(enumName, caseName) + } + + if let baseExpr = memberExpr.base?.as(DeclReferenceExprSyntax.self) { + let baseName = baseExpr.baseName.text + let lastComponent = enumName.split(separator: ".").last.map(String.init) ?? enumName + if baseName == enumName || baseName == lastComponent { + return .enumCase(enumName, caseName) + } + } + + return nil + } + + /// Extracts default value from parameter's default value clause + private func extractDefaultValue( + from defaultClause: InitializerClauseSyntax?, + type: BridgeType + ) -> DefaultValue? { + guard let defaultClause = defaultClause else { + return nil + } + + if !isSupportedDefaultValueExpression(defaultClause) { + diagnose( + node: defaultClause, + message: "Complex default parameter expressions are not supported", + hint: "Use simple literal values (e.g., \"text\", 42, true, nil) or simple constants" + ) + return nil + } + + let expr = defaultClause.value + + if expr.is(NilLiteralExprSyntax.self) { + guard case .optional(_) = type else { + diagnose( + node: expr, + message: "nil is only valid for optional parameters", + hint: "Make the parameter optional by adding ? to the type" + ) + return nil + } + return .null + } + + if let memberExpr = expr.as(MemberAccessExprSyntax.self), + let enumValue = extractEnumCaseValue(from: memberExpr, type: type) + { + return enumValue + } + + if let funcCall = expr.as(FunctionCallExprSyntax.self) { + return extractConstructorDefaultValue(from: funcCall, type: type) + } + + if let literalValue = extractLiteralValue(from: expr, type: type) { + return literalValue + } + + diagnose( + node: expr, + message: "Unsupported default parameter value expression", + hint: "Use simple literal values like \"text\", 42, true, false, nil, or enum cases like .caseName" + ) + return nil + } + + /// Extracts default value from a constructor call expression + private func extractConstructorDefaultValue( + from funcCall: FunctionCallExprSyntax, + type: BridgeType + ) -> DefaultValue? { + guard let calledExpr = funcCall.calledExpression.as(DeclReferenceExprSyntax.self) else { + diagnose( + node: funcCall, + message: "Complex constructor expressions are not supported", + hint: "Use a simple constructor call like ClassName() or ClassName(arg: value)" + ) + return nil + } + + let className = calledExpr.baseName.text + let expectedClassName: String? + switch type { + case .swiftHeapObject(let name): + expectedClassName = name.split(separator: ".").last.map(String.init) + case .optional(.swiftHeapObject(let name)): + expectedClassName = name.split(separator: ".").last.map(String.init) + default: + diagnose( + node: funcCall, + message: "Constructor calls are only supported for class types", + hint: "Parameter type should be a Swift class" + ) + return nil + } + + guard let expectedClassName = expectedClassName, className == expectedClassName else { + diagnose( + node: funcCall, + message: "Constructor class name '\(className)' doesn't match parameter type", + hint: "Ensure the constructor matches the parameter type" + ) + return nil + } + + if funcCall.arguments.isEmpty { + return .object(className) + } + + var constructorArgs: [DefaultValue] = [] + for argument in funcCall.arguments { + guard let argValue = extractLiteralValue(from: argument.expression) else { + diagnose( + node: argument.expression, + message: "Constructor argument must be a literal value", + hint: "Use simple literals like \"text\", 42, true, false in constructor arguments" + ) + return nil + } + + constructorArgs.append(argValue) + } + + return .objectWithArguments(className, constructorArgs) + } + + /// Extracts a literal value from an expression with optional type checking + private func extractLiteralValue(from expr: ExprSyntax, type: BridgeType? = nil) -> DefaultValue? { + if expr.is(NilLiteralExprSyntax.self) { + return .null + } + + if let stringLiteral = expr.as(StringLiteralExprSyntax.self), + let segment = stringLiteral.segments.first?.as(StringSegmentSyntax.self) + { + let value = DefaultValue.string(segment.content.text) + if let type = type, !type.isCompatibleWith(.string) { + return nil + } + return value + } + + if let boolLiteral = expr.as(BooleanLiteralExprSyntax.self) { + let value = DefaultValue.bool(boolLiteral.literal.text == "true") + if let type = type, !type.isCompatibleWith(.bool) { + return nil + } + return value + } + + var numericExpr = expr + var isNegative = false + if let prefixExpr = expr.as(PrefixOperatorExprSyntax.self), + prefixExpr.operator.text == "-" + { + numericExpr = prefixExpr.expression + isNegative = true + } + + if let intLiteral = numericExpr.as(IntegerLiteralExprSyntax.self), + let intValue = Int(intLiteral.literal.text) + { + let value = DefaultValue.int(isNegative ? -intValue : intValue) + if let type = type, !type.isCompatibleWith(.int) { + return nil + } + return value + } + + if let floatLiteral = numericExpr.as(FloatLiteralExprSyntax.self) { + if let floatValue = Float(floatLiteral.literal.text) { + let value = DefaultValue.float(isNegative ? -floatValue : floatValue) + if type == nil || type?.isCompatibleWith(.float) == true { + return value + } + } + if let doubleValue = Double(floatLiteral.literal.text) { + let value = DefaultValue.double(isNegative ? -doubleValue : doubleValue) + if type == nil || type?.isCompatibleWith(.double) == true { + return value + } + } + } + + return nil + } + override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind { guard node.attributes.hasJSAttribute() else { return .skipChildren @@ -252,7 +486,10 @@ public class ExportSwift { let name = param.secondName?.text ?? param.firstName.text let label = param.firstName.text - parameters.append(Parameter(label: label, name: name, type: type)) + + let defaultValue = extractDefaultValue(from: param.defaultValue, type: type) + + parameters.append(Parameter(label: label, name: name, type: type, defaultValue: defaultValue)) } let returnType: BridgeType if let returnClause = node.signature.returnClause { @@ -409,7 +646,10 @@ public class ExportSwift { } let name = param.secondName?.text ?? param.firstName.text let label = param.firstName.text - parameters.append(Parameter(label: label, name: name, type: type)) + + let defaultValue = extractDefaultValue(from: param.defaultValue, type: type) + + parameters.append(Parameter(label: label, name: name, type: type, defaultValue: defaultValue)) } guard let effects = collectEffects(signature: node.signature) else { @@ -630,7 +870,7 @@ public class ExportSwift { swiftCallName: swiftCallName, explicitAccessControl: explicitAccessControl, cases: [], // Will be populated in visit(EnumCaseDeclSyntax) - rawType: rawType, + rawType: SwiftEnumRawType(rawType), namespace: effectiveNamespace, emitStyle: emitStyle, staticMethods: [], @@ -668,9 +908,7 @@ public class ExportSwift { if case .tsEnum = emitStyle { // Check for Bool raw type limitation - if let raw = exportedEnum.rawType, - let rawEnum = SwiftEnumRawType.from(raw), rawEnum == .bool - { + if exportedEnum.rawType == .bool { diagnose( node: jsAttribute, message: "TypeScript enum style is not supported for Bool raw-value enums", @@ -925,7 +1163,7 @@ public class ExportSwift { return Constants.supportedRawTypes.contains(typeName) }?.type.trimmedDescription - if let rawTypeString, let rawType = SwiftEnumRawType.from(rawTypeString) { + if let rawType = SwiftEnumRawType(rawTypeString) { return .rawValueEnum(swiftCallName, rawType) } else { let hasAnyCases = enumDecl.memberBlock.members.contains { member in @@ -1903,3 +2141,17 @@ extension WithModifiersSyntax { } } } + +fileprivate extension BridgeType { + /// Returns true if a value of `expectedType` can be assigned to this type. + func isCompatibleWith(_ expectedType: BridgeType) -> Bool { + switch (self, expectedType) { + case let (lhs, rhs) where lhs == rhs: + return true + case (.optional(let wrapped), expectedType): + return wrapped == expectedType + default: + return false + } + } +} diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift index 42bfef50..087cf17d 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift @@ -1026,6 +1026,16 @@ struct BridgeJSLink { ] } + func generateParameterList(parameters: [Parameter]) -> String { + parameters.map { param in + if let defaultValue = param.defaultValue { + let defaultJs = DefaultValueGenerator().generate(defaultValue, format: .javascript) + return "\(param.name) = \(defaultJs)" + } + return param.name + }.joined(separator: ", ") + } + func renderFunction( name: String, parameters: [Parameter], @@ -1034,8 +1044,10 @@ struct BridgeJSLink { ) -> [String] { let printer = CodeFragmentPrinter() + let parameterList = generateParameterList(parameters: parameters) + printer.write( - "\(declarationPrefixKeyword.map { "\($0) "} ?? "")\(name)(\(parameters.map { $0.name }.joined(separator: ", "))) {" + "\(declarationPrefixKeyword.map { "\($0) "} ?? "")\(name)(\(parameterList)) {" ) printer.indent { printer.write(contentsOf: body) @@ -1058,8 +1070,83 @@ struct BridgeJSLink { } else { returnTypeWithEffect = returnType.tsType } - return - "(\(parameters.map { "\($0.name): \($0.type.tsType)" }.joined(separator: ", "))): \(returnTypeWithEffect)" + let parameterSignatures = parameters.map { param in + let optional = param.hasDefault ? "?" : "" + return "\(param.name)\(optional): \(param.type.tsType)" + } + return "(\(parameterSignatures.joined(separator: ", "))): \(returnTypeWithEffect)" + } + + /// Helper method to append JSDoc comments for parameters with default values + private func appendJSDocIfNeeded(for parameters: [Parameter], to lines: inout [String]) { + let jsDocLines = DefaultValueGenerator().generateJSDoc(for: parameters) + lines.append(contentsOf: jsDocLines) + } + + /// Helper struct for generating default value representations + private struct DefaultValueGenerator { + enum OutputFormat { + case javascript + case typescript + } + + /// Generates default value representation for JavaScript or TypeScript + func generate(_ defaultValue: DefaultValue, format: OutputFormat) -> String { + switch defaultValue { + case .string(let value): + let escapedValue = + format == .javascript + ? escapeForJavaScript(value) + : value // TypeScript doesn't need escape in doc comments + return "\"\(escapedValue)\"" + case .int(let value): + return "\(value)" + case .float(let value): + return "\(value)" + case .double(let value): + return "\(value)" + case .bool(let value): + return value ? "true" : "false" + case .null: + return "null" + case .enumCase(let enumName, let caseName): + let simpleName = enumName.components(separatedBy: ".").last ?? enumName + let jsEnumName = format == .javascript ? "\(simpleName)Values" : simpleName + return "\(jsEnumName).\(caseName.capitalizedFirstLetter)" + case .object(let className): + return "new \(className)()" + case .objectWithArguments(let className, let args): + let argStrings = args.map { arg in + generate(arg, format: format) + } + return "new \(className)(\(argStrings.joined(separator: ", ")))" + } + } + + private func escapeForJavaScript(_ string: String) -> String { + return + string + .replacingOccurrences(of: "\\", with: "\\\\") + .replacingOccurrences(of: "\"", with: "\\\"") + } + + /// Generates JSDoc comment lines for parameters with default values + func generateJSDoc(for parameters: [Parameter]) -> [String] { + let paramsWithDefaults = parameters.filter { $0.hasDefault } + guard !paramsWithDefaults.isEmpty else { + return [] + } + + var jsDocLines: [String] = ["/**"] + for param in paramsWithDefaults { + if let defaultValue = param.defaultValue { + let defaultDoc = generate(defaultValue, format: .typescript) + jsDocLines.append(" * @param \(param.name) - Optional parameter (default: \(defaultDoc))") + } + } + jsDocLines.append(" */") + return jsDocLines + } } func renderExportedEnum(_ enumDefinition: ExportedEnum) throws -> (js: [String], dts: [String]) { @@ -1110,9 +1197,7 @@ struct BridgeJSLink { printer.indent { for (index, enumCase) in enumDefinition.cases.enumerated() { let caseName = enumCase.name.capitalizedFirstLetter - let value = getEnumCaseValue( - enumCase: enumCase, - enumType: enumDefinition.enumType, + let value = enumCase.jsValue( rawType: enumDefinition.rawType, index: index ) @@ -1132,9 +1217,7 @@ struct BridgeJSLink { printer.indent { for (index, enumCase) in enumDefinition.cases.enumerated() { let caseName = enumCase.name.capitalizedFirstLetter - let value = getEnumCaseValue( - enumCase: enumCase, - enumType: enumDefinition.enumType, + let value = enumCase.jsValue( rawType: enumDefinition.rawType, index: index ) @@ -1192,17 +1275,6 @@ struct BridgeJSLink { return printer.lines } - private func getEnumCaseValue(enumCase: EnumCase, enumType: EnumType, rawType: String?, index: Int) -> String { - switch enumType { - case .simple: - return "\(index)" - case .rawValue: - let rawValue = enumCase.rawValue ?? enumCase.name - return SwiftEnumRawType.formatValue(rawValue, rawType: rawType ?? "") - case .associatedValue, .namespace: - return "" - } - } } extension BridgeJSLink { @@ -1224,6 +1296,9 @@ extension BridgeJSLink { declarationPrefixKeyword: "function" ) var dtsLines: [String] = [] + + appendJSDocIfNeeded(for: function.parameters, to: &dtsLines) + dtsLines.append( "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" ) @@ -1266,9 +1341,13 @@ extension BridgeJSLink { declarationPrefixKeyword: "static" ) - let dtsLines = [ + var dtsLines: [String] = [] + + appendJSDocIfNeeded(for: function.parameters, to: &dtsLines) + + dtsLines.append( "static \(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" - ] + ) return (funcLines, dtsLines) } @@ -1295,9 +1374,13 @@ extension BridgeJSLink { } printer.write("},") - let dtsLines = [ + var dtsLines: [String] = [] + + appendJSDocIfNeeded(for: function.parameters, to: &dtsLines) + + dtsLines.append( "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType, effects: function.effects));" - ] + ) return (printer.lines, dtsLines) } @@ -1333,10 +1416,11 @@ extension BridgeJSLink { try thunkBuilder.lowerParameter(param: param) } let returnExpr = try thunkBuilder.call(abiName: function.abiName, returnType: function.returnType) + let paramList = thunkBuilder.generateParameterList(parameters: function.parameters) let printer = CodeFragmentPrinter() printer.write( - "\(enumName).\(function.name) = function(\(function.parameters.map { $0.name }.joined(separator: ", "))) {" + "\(enumName).\(function.name) = function(\(paramList)) {" ) printer.indent { printer.write(contentsOf: thunkBuilder.body) @@ -1521,8 +1605,10 @@ extension BridgeJSLink { try thunkBuilder.lowerParameter(param: param) } + let constructorParamList = thunkBuilder.generateParameterList(parameters: constructor.parameters) + jsPrinter.indent { - jsPrinter.write("constructor(\(constructor.parameters.map { $0.name }.joined(separator: ", "))) {") + jsPrinter.write("constructor(\(constructorParamList)) {") let returnExpr = thunkBuilder.callConstructor(abiName: constructor.abiName) jsPrinter.indent { jsPrinter.write(contentsOf: thunkBuilder.body) @@ -1534,6 +1620,10 @@ extension BridgeJSLink { } dtsExportEntryPrinter.indent { + let jsDocLines = DefaultValueGenerator().generateJSDoc(for: constructor.parameters) + for line in jsDocLines { + dtsExportEntryPrinter.write(line) + } dtsExportEntryPrinter.write( "new\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name), effects: constructor.effects));" ) @@ -2045,22 +2135,26 @@ extension BridgeJSLink { case .tsEnum: printer.write("enum \(enumDefinition.name) {") printer.indent { - for enumCase in enumDefinition.cases { + for (index, enumCase) in enumDefinition.cases.enumerated() { let caseName = enumCase.name.capitalizedFirstLetter - let rawValue = enumCase.rawValue ?? enumCase.name - let formattedValue = SwiftEnumRawType.formatValue(rawValue, rawType: rawType) - printer.write("\(caseName) = \(formattedValue),") + let value = enumCase.jsValue( + rawType: rawType, + index: index + ) + printer.write("\(caseName) = \(value),") } } printer.write("}") case .const: printer.write("const \(enumValuesName): {") printer.indent { - for enumCase in enumDefinition.cases { + for (index, enumCase) in enumDefinition.cases.enumerated() { let caseName = enumCase.name.capitalizedFirstLetter - let rawValue = enumCase.rawValue ?? enumCase.name - let formattedValue = SwiftEnumRawType.formatValue(rawValue, rawType: rawType) - printer.write("readonly \(caseName): \(formattedValue);") + let value = enumCase.jsValue( + rawType: rawType, + index: index + ) + printer.write("readonly \(caseName): \(value);") } } printer.write("};") diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift index 8b0f3f47..9d0d9162 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSLink/JSGlueGen.swift @@ -623,7 +623,11 @@ struct IntrinsicJSFragment: Sendable { printer.indent { for (index, enumCase) in enumDefinition.cases.enumerated() { let caseName = enumCase.name.capitalizedFirstLetter - printer.write("\(caseName): \(index),") + let value = enumCase.jsValue( + rawType: enumDefinition.rawType, + index: index + ) + printer.write("\(caseName): \(value),") } } printer.write("};") @@ -641,15 +645,13 @@ struct IntrinsicJSFragment: Sendable { let enumName = arguments[0] printer.write("const \(enumName) = {") printer.indent { - for enumCase in enumDefinition.cases { + for (index, enumCase) in enumDefinition.cases.enumerated() { let caseName = enumCase.name.capitalizedFirstLetter - let rawValue = enumCase.rawValue ?? enumCase.name - let formattedValue = SwiftEnumRawType.formatValue( - rawValue, - rawType: enumDefinition.rawType ?? "" + let value = enumCase.jsValue( + rawType: enumDefinition.rawType, + index: index ) - - printer.write("\(caseName): \(formattedValue),") + printer.write("\(caseName): \(value),") } } printer.write("};") diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift index e581fa40..84595782 100644 --- a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift +++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift @@ -107,35 +107,43 @@ public enum SwiftEnumRawType: String, CaseIterable, Codable, Sendable { } } - public static func from(_ rawTypeString: String) -> SwiftEnumRawType? { - return Self.allCases.first { $0.rawValue == rawTypeString } - } - - public static func formatValue(_ rawValue: String, rawType: String) -> String { - if let enumType = from(rawType) { - switch enumType { - case .string: - return "\"\(rawValue)\"" - case .bool: - return rawValue.lowercased() == "true" ? "true" : "false" - case .float, .double, .int, .int32, .int64, .uint, .uint32, .uint64: - return rawValue - } - } else { - return rawValue + public init?(_ rawTypeString: String?) { + guard let rawTypeString = rawTypeString, + let match = Self.allCases.first(where: { $0.rawValue == rawTypeString }) + else { + return nil } + self = match } } +public enum DefaultValue: Codable, Equatable, Sendable { + case string(String) + case int(Int) + case float(Float) + case double(Double) + case bool(Bool) + case null + case enumCase(String, String) // enumName, caseName + case object(String) // className for parameterless constructor + case objectWithArguments(String, [DefaultValue]) // className, constructor argument values +} + public struct Parameter: Codable, Equatable, Sendable { public let label: String? public let name: String public let type: BridgeType + public let defaultValue: DefaultValue? - public init(label: String?, name: String, type: BridgeType) { + public var hasDefault: Bool { + return defaultValue != nil + } + + public init(label: String?, name: String, type: BridgeType, defaultValue: DefaultValue? = nil) { self.label = label self.name = name self.type = type + self.defaultValue = defaultValue } } @@ -186,6 +194,24 @@ public struct EnumCase: Codable, Equatable, Sendable { } } +extension EnumCase { + /// Generates JavaScript/TypeScript value representation for this enum case + public func jsValue(rawType: SwiftEnumRawType?, index: Int) -> String { + guard let rawType = rawType else { + return "\(index)" + } + let rawValue = self.rawValue ?? self.name + switch rawType { + case .string: + return "\"\(rawValue)\"" + case .bool: + return rawValue.lowercased() == "true" ? "true" : "false" + case .float, .double, .int, .int32, .int64, .uint, .uint32, .uint64: + return rawValue + } + } +} + public enum EnumEmitStyle: String, Codable, Sendable { case const case tsEnum @@ -196,7 +222,7 @@ public struct ExportedEnum: Codable, Equatable, Sendable { public let swiftCallName: String public let explicitAccessControl: String? public var cases: [EnumCase] - public let rawType: String? + public let rawType: SwiftEnumRawType? public let namespace: [String]? public let emitStyle: EnumEmitStyle public var staticMethods: [ExportedFunction] @@ -216,7 +242,7 @@ public struct ExportedEnum: Codable, Equatable, Sendable { swiftCallName: String, explicitAccessControl: String?, cases: [EnumCase], - rawType: String?, + rawType: SwiftEnumRawType?, namespace: [String]?, emitStyle: EnumEmitStyle, staticMethods: [ExportedFunction] = [], diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/DefaultParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/DefaultParameters.swift new file mode 100644 index 00000000..b670d2db --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/DefaultParameters.swift @@ -0,0 +1,74 @@ +@JS public func testStringDefault(message: String = "Hello World") -> String + +@JS public func testNegativeIntDefault(value: Int = -42) -> Int + +@JS public func testBoolDefault(flag: Bool = true) -> Bool + +@JS public func testNegativeFloatDefault(temp: Float = -273.15) -> Float + +@JS public func testDoubleDefault(precision: Double = 2.718) -> Double + +@JS public func testOptionalDefault(name: String? = nil) -> String? + +@JS public func testOptionalStringDefault(greeting: String? = "Hi") -> String? + +@JS public func testMultipleDefaults( + title: String = "Default Title", + count: Int = 10, + enabled: Bool = false +) -> String + +@JS public enum Status { + case active + case inactive + case pending +} + +@JS public func testEnumDefault(status: Status = .active) -> Status + +@JS class DefaultGreeter { + @JS var name: String + @JS init(name: String) { + self.name = name + } +} + +@JS class EmptyGreeter { + @JS init() {} +} + +@JS public func testComplexInit(greeter: DefaultGreeter = DefaultGreeter(name: "DefaultUser")) -> DefaultGreeter +@JS public func testEmptyInit(greeter: EmptyGreeter = EmptyGreeter()) -> EmptyGreeter + +@JS class ConstructorDefaults { + @JS var name: String + @JS var count: Int + @JS var enabled: Bool + @JS var status: Status + @JS var tag: String? + + @JS init( + name: String = "Default", + count: Int = 42, + enabled: Bool = true, + status: Status = .active, + tag: String? = nil + ) { + self.name = name + self.count = count + self.enabled = enabled + self.status = status + self.tag = tag + } + + @JS func describe() -> String { + let tagStr = tag ?? "nil" + let statusStr: String + switch status { + case .active: statusStr = "active" + case .inactive: statusStr = "inactive" + case .pending: statusStr = "pending" + } + return "\(name):\(count):\(enabled):\(statusStr):\(tagStr)" + } +} diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.Export.d.ts new file mode 100644 index 00000000..38cbf989 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.Export.d.ts @@ -0,0 +1,109 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export const StatusValues: { + readonly Active: 0; + readonly Inactive: 1; + readonly Pending: 2; +}; +export type StatusTag = typeof StatusValues[keyof typeof StatusValues]; + +export type StatusObject = typeof StatusValues; + +/// Represents a Swift heap object like a class instance or an actor instance. +export interface SwiftHeapObject { + /// Release the heap object. + /// + /// Note: Calling this method will release the heap object and it will no longer be accessible. + release(): void; +} +export interface DefaultGreeter extends SwiftHeapObject { + name: string; +} +export interface EmptyGreeter extends SwiftHeapObject { +} +export interface ConstructorDefaults extends SwiftHeapObject { + describe(): string; + name: string; + count: number; + enabled: boolean; + status: StatusTag; + tag: string | null; +} +export type Exports = { + DefaultGreeter: { + new(name: string): DefaultGreeter; + } + EmptyGreeter: { + new(): EmptyGreeter; + } + ConstructorDefaults: { + /** + * @param name - Optional parameter (default: "Default") + * @param count - Optional parameter (default: 42) + * @param enabled - Optional parameter (default: true) + * @param status - Optional parameter (default: Status.Active) + * @param tag - Optional parameter (default: null) + */ + new(name?: string, count?: number, enabled?: boolean, status?: StatusTag, tag?: string | null): ConstructorDefaults; + } + /** + * @param message - Optional parameter (default: "Hello World") + */ + testStringDefault(message?: string): string; + /** + * @param value - Optional parameter (default: -42) + */ + testNegativeIntDefault(value?: number): number; + /** + * @param flag - Optional parameter (default: true) + */ + testBoolDefault(flag?: boolean): boolean; + /** + * @param temp - Optional parameter (default: -273.15) + */ + testNegativeFloatDefault(temp?: number): number; + /** + * @param precision - Optional parameter (default: 2.718) + */ + testDoubleDefault(precision?: number): number; + /** + * @param name - Optional parameter (default: null) + */ + testOptionalDefault(name?: string | null): string | null; + /** + * @param greeting - Optional parameter (default: "Hi") + */ + testOptionalStringDefault(greeting?: string | null): string | null; + /** + * @param title - Optional parameter (default: "Default Title") + * @param count - Optional parameter (default: 10) + * @param enabled - Optional parameter (default: false) + */ + testMultipleDefaults(title?: string, count?: number, enabled?: boolean): string; + /** + * @param status - Optional parameter (default: Status.Active) + */ + testEnumDefault(status?: StatusTag): StatusTag; + /** + * @param greeter - Optional parameter (default: new DefaultGreeter("DefaultUser")) + */ + testComplexInit(greeter?: DefaultGreeter): DefaultGreeter; + /** + * @param greeter - Optional parameter (default: new EmptyGreeter()) + */ + testEmptyInit(greeter?: EmptyGreeter): EmptyGreeter; + Status: StatusObject +} +export type Imports = { +} +export function createInstantiator(options: { + imports: Imports; +}, swift: any): Promise<{ + addImports: (importObject: WebAssembly.Imports) => void; + setInstance: (instance: WebAssembly.Instance) => void; + createExports: (instance: WebAssembly.Instance) => Exports; +}>; \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.Export.js new file mode 100644 index 00000000..dac7bdfc --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/DefaultParameters.Export.js @@ -0,0 +1,392 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +export const StatusValues = { + Active: 0, + Inactive: 1, + Pending: 2, +}; + +export async function createInstantiator(options, swift) { + let instance; + let memory; + let setException; + const textDecoder = new TextDecoder("utf-8"); + const textEncoder = new TextEncoder("utf-8"); + let tmpRetString; + let tmpRetBytes; + let tmpRetException; + let tmpRetOptionalBool; + let tmpRetOptionalInt; + let tmpRetOptionalFloat; + let tmpRetOptionalDouble; + let tmpRetOptionalHeapObject; + let tmpRetTag; + let tmpRetStrings = []; + let tmpRetInts = []; + let tmpRetF32s = []; + let tmpRetF64s = []; + let tmpParamInts = []; + let tmpParamF32s = []; + let tmpParamF64s = []; + + return { + /** + * @param {WebAssembly.Imports} importObject + */ + addImports: (importObject, importsContext) => { + const bjs = {}; + importObject["bjs"] = bjs; + const imports = options.getImports(importsContext); + bjs["swift_js_return_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + bjs["swift_js_init_memory"] = function(sourceId, bytesPtr) { + const source = swift.memory.getObject(sourceId); + const bytes = new Uint8Array(memory.buffer, bytesPtr); + bytes.set(source); + } + bjs["swift_js_make_js_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + return swift.memory.retain(textDecoder.decode(bytes)); + } + bjs["swift_js_init_memory_with_result"] = function(ptr, len) { + const target = new Uint8Array(memory.buffer, ptr, len); + target.set(tmpRetBytes); + tmpRetBytes = undefined; + } + bjs["swift_js_throw"] = function(id) { + tmpRetException = swift.memory.retainByRef(id); + } + bjs["swift_js_retain"] = function(id) { + return swift.memory.retainByRef(id); + } + bjs["swift_js_release"] = function(id) { + swift.memory.release(id); + } + bjs["swift_js_push_tag"] = function(tag) { + tmpRetTag = tag; + } + bjs["swift_js_push_int"] = function(v) { + tmpRetInts.push(v | 0); + } + bjs["swift_js_push_f32"] = function(v) { + tmpRetF32s.push(Math.fround(v)); + } + bjs["swift_js_push_f64"] = function(v) { + tmpRetF64s.push(v); + } + bjs["swift_js_push_string"] = function(ptr, len) { + const bytes = new Uint8Array(memory.buffer, ptr, len); + const value = textDecoder.decode(bytes); + tmpRetStrings.push(value); + } + bjs["swift_js_pop_param_int32"] = function() { + return tmpParamInts.pop(); + } + bjs["swift_js_pop_param_f32"] = function() { + return tmpParamF32s.pop(); + } + bjs["swift_js_pop_param_f64"] = function() { + return tmpParamF64s.pop(); + } + bjs["swift_js_return_optional_bool"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalBool = null; + } else { + tmpRetOptionalBool = value !== 0; + } + } + bjs["swift_js_return_optional_int"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalInt = null; + } else { + tmpRetOptionalInt = value | 0; + } + } + bjs["swift_js_return_optional_float"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalFloat = null; + } else { + tmpRetOptionalFloat = Math.fround(value); + } + } + bjs["swift_js_return_optional_double"] = function(isSome, value) { + if (isSome === 0) { + tmpRetOptionalDouble = null; + } else { + tmpRetOptionalDouble = value; + } + } + bjs["swift_js_return_optional_string"] = function(isSome, ptr, len) { + if (isSome === 0) { + tmpRetString = null; + } else { + const bytes = new Uint8Array(memory.buffer, ptr, len); + tmpRetString = textDecoder.decode(bytes); + } + } + bjs["swift_js_return_optional_object"] = function(isSome, objectId) { + if (isSome === 0) { + tmpRetString = null; + } else { + tmpRetString = swift.memory.getObject(objectId); + } + } + bjs["swift_js_return_optional_heap_object"] = function(isSome, pointer) { + if (isSome === 0) { + tmpRetOptionalHeapObject = null; + } else { + tmpRetOptionalHeapObject = pointer; + } + } + // Wrapper functions for module: TestModule + if (!importObject["TestModule"]) { + importObject["TestModule"] = {}; + } + importObject["TestModule"]["bjs_DefaultGreeter_wrap"] = function(pointer) { + const obj = DefaultGreeter.__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_EmptyGreeter_wrap"] = function(pointer) { + const obj = EmptyGreeter.__construct(pointer); + return swift.memory.retain(obj); + }; + importObject["TestModule"]["bjs_ConstructorDefaults_wrap"] = function(pointer) { + const obj = ConstructorDefaults.__construct(pointer); + return swift.memory.retain(obj); + }; + }, + setInstance: (i) => { + instance = i; + memory = instance.exports.memory; + + setException = (error) => { + instance.exports._swift_js_exception.value = swift.memory.retain(error) + } + }, + /** @param {WebAssembly.Instance} instance */ + createExports: (instance) => { + const js = swift.memory.heap; + /// Represents a Swift heap object like a class instance or an actor instance. + class SwiftHeapObject { + static __wrap(pointer, deinit, prototype) { + const obj = Object.create(prototype); + obj.pointer = pointer; + obj.hasReleased = false; + obj.deinit = deinit; + obj.registry = new FinalizationRegistry((pointer) => { + deinit(pointer); + }); + obj.registry.register(this, obj.pointer); + return obj; + } + + release() { + this.registry.unregister(this); + this.deinit(this.pointer); + } + } + class DefaultGreeter extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_DefaultGreeter_deinit, DefaultGreeter.prototype); + } + + constructor(name) { + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + const ret = instance.exports.bjs_DefaultGreeter_init(nameId, nameBytes.length); + swift.memory.release(nameId); + return DefaultGreeter.__construct(ret); + } + get name() { + instance.exports.bjs_DefaultGreeter_name_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set name(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_DefaultGreeter_name_set(this.pointer, valueId, valueBytes.length); + swift.memory.release(valueId); + } + } + class EmptyGreeter extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_EmptyGreeter_deinit, EmptyGreeter.prototype); + } + + constructor() { + const ret = instance.exports.bjs_EmptyGreeter_init(); + return EmptyGreeter.__construct(ret); + } + } + class ConstructorDefaults extends SwiftHeapObject { + static __construct(ptr) { + return SwiftHeapObject.__wrap(ptr, instance.exports.bjs_ConstructorDefaults_deinit, ConstructorDefaults.prototype); + } + + constructor(name = "Default", count = 42, enabled = true, status = StatusValues.Active, tag = null) { + const nameBytes = textEncoder.encode(name); + const nameId = swift.memory.retain(nameBytes); + const isSome = tag != null; + let tagId, tagBytes; + if (isSome) { + tagBytes = textEncoder.encode(tag); + tagId = swift.memory.retain(tagBytes); + } + const ret = instance.exports.bjs_ConstructorDefaults_init(nameId, nameBytes.length, count, enabled, status, +isSome, isSome ? tagId : 0, isSome ? tagBytes.length : 0); + swift.memory.release(nameId); + if (tagId != undefined) { + swift.memory.release(tagId); + } + return ConstructorDefaults.__construct(ret); + } + describe() { + instance.exports.bjs_ConstructorDefaults_describe(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + get name() { + instance.exports.bjs_ConstructorDefaults_name_get(this.pointer); + const ret = tmpRetString; + tmpRetString = undefined; + return ret; + } + set name(value) { + const valueBytes = textEncoder.encode(value); + const valueId = swift.memory.retain(valueBytes); + instance.exports.bjs_ConstructorDefaults_name_set(this.pointer, valueId, valueBytes.length); + swift.memory.release(valueId); + } + get count() { + const ret = instance.exports.bjs_ConstructorDefaults_count_get(this.pointer); + return ret; + } + set count(value) { + instance.exports.bjs_ConstructorDefaults_count_set(this.pointer, value); + } + get enabled() { + const ret = instance.exports.bjs_ConstructorDefaults_enabled_get(this.pointer); + return ret !== 0; + } + set enabled(value) { + instance.exports.bjs_ConstructorDefaults_enabled_set(this.pointer, value); + } + get status() { + const ret = instance.exports.bjs_ConstructorDefaults_status_get(this.pointer); + return ret; + } + set status(value) { + instance.exports.bjs_ConstructorDefaults_status_set(this.pointer, value); + } + get tag() { + instance.exports.bjs_ConstructorDefaults_tag_get(this.pointer); + const optResult = tmpRetString; + tmpRetString = undefined; + return optResult; + } + set tag(value) { + const isSome = value != null; + let valueId, valueBytes; + if (isSome) { + valueBytes = textEncoder.encode(value); + valueId = swift.memory.retain(valueBytes); + } + instance.exports.bjs_ConstructorDefaults_tag_set(this.pointer, +isSome, isSome ? valueId : 0, isSome ? valueBytes.length : 0); + if (valueId != undefined) { + swift.memory.release(valueId); + } + } + } + return { + DefaultGreeter, + EmptyGreeter, + ConstructorDefaults, + testStringDefault: function bjs_testStringDefault(message = "Hello World") { + const messageBytes = textEncoder.encode(message); + const messageId = swift.memory.retain(messageBytes); + instance.exports.bjs_testStringDefault(messageId, messageBytes.length); + const ret = tmpRetString; + tmpRetString = undefined; + swift.memory.release(messageId); + return ret; + }, + testNegativeIntDefault: function bjs_testNegativeIntDefault(value = -42) { + const ret = instance.exports.bjs_testNegativeIntDefault(value); + return ret; + }, + testBoolDefault: function bjs_testBoolDefault(flag = true) { + const ret = instance.exports.bjs_testBoolDefault(flag); + return ret !== 0; + }, + testNegativeFloatDefault: function bjs_testNegativeFloatDefault(temp = -273.15) { + const ret = instance.exports.bjs_testNegativeFloatDefault(temp); + return ret; + }, + testDoubleDefault: function bjs_testDoubleDefault(precision = 2.718) { + const ret = instance.exports.bjs_testDoubleDefault(precision); + return ret; + }, + testOptionalDefault: function bjs_testOptionalDefault(name = null) { + const isSome = name != null; + let nameId, nameBytes; + if (isSome) { + nameBytes = textEncoder.encode(name); + nameId = swift.memory.retain(nameBytes); + } + instance.exports.bjs_testOptionalDefault(+isSome, isSome ? nameId : 0, isSome ? nameBytes.length : 0); + const optResult = tmpRetString; + tmpRetString = undefined; + if (nameId != undefined) { + swift.memory.release(nameId); + } + return optResult; + }, + testOptionalStringDefault: function bjs_testOptionalStringDefault(greeting = "Hi") { + const isSome = greeting != null; + let greetingId, greetingBytes; + if (isSome) { + greetingBytes = textEncoder.encode(greeting); + greetingId = swift.memory.retain(greetingBytes); + } + instance.exports.bjs_testOptionalStringDefault(+isSome, isSome ? greetingId : 0, isSome ? greetingBytes.length : 0); + const optResult = tmpRetString; + tmpRetString = undefined; + if (greetingId != undefined) { + swift.memory.release(greetingId); + } + return optResult; + }, + testMultipleDefaults: function bjs_testMultipleDefaults(title = "Default Title", count = 10, enabled = false) { + const titleBytes = textEncoder.encode(title); + const titleId = swift.memory.retain(titleBytes); + instance.exports.bjs_testMultipleDefaults(titleId, titleBytes.length, count, enabled); + const ret = tmpRetString; + tmpRetString = undefined; + swift.memory.release(titleId); + return ret; + }, + testEnumDefault: function bjs_testEnumDefault(status = StatusValues.Active) { + const ret = instance.exports.bjs_testEnumDefault(status); + return ret; + }, + testComplexInit: function bjs_testComplexInit(greeter = new DefaultGreeter("DefaultUser")) { + const ret = instance.exports.bjs_testComplexInit(greeter.pointer); + return DefaultGreeter.__construct(ret); + }, + testEmptyInit: function bjs_testEmptyInit(greeter = new EmptyGreeter()) { + const ret = instance.exports.bjs_testEmptyInit(greeter.pointer); + return EmptyGreeter.__construct(ret); + }, + Status: StatusValues, + }; + }, + } +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/DefaultParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/DefaultParameters.json new file mode 100644 index 00000000..edfd4e57 --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/DefaultParameters.json @@ -0,0 +1,646 @@ +{ + "classes" : [ + { + "constructor" : { + "abiName" : "bjs_DefaultGreeter_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "label" : "name", + "name" : "name", + "type" : { + "string" : { + + } + } + } + ] + }, + "methods" : [ + + ], + "name" : "DefaultGreeter", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "name", + "type" : { + "string" : { + + } + } + } + ], + "swiftCallName" : "DefaultGreeter" + }, + { + "constructor" : { + "abiName" : "bjs_EmptyGreeter_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + + ] + }, + "methods" : [ + + ], + "name" : "EmptyGreeter", + "properties" : [ + + ], + "swiftCallName" : "EmptyGreeter" + }, + { + "constructor" : { + "abiName" : "bjs_ConstructorDefaults_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "defaultValue" : { + "string" : { + "_0" : "Default" + } + }, + "label" : "name", + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "defaultValue" : { + "int" : { + "_0" : 42 + } + }, + "label" : "count", + "name" : "count", + "type" : { + "int" : { + + } + } + }, + { + "defaultValue" : { + "bool" : { + "_0" : true + } + }, + "label" : "enabled", + "name" : "enabled", + "type" : { + "bool" : { + + } + } + }, + { + "defaultValue" : { + "enumCase" : { + "_0" : "Status", + "_1" : "active" + } + }, + "label" : "status", + "name" : "status", + "type" : { + "caseEnum" : { + "_0" : "Status" + } + } + }, + { + "defaultValue" : { + "null" : { + + } + }, + "label" : "tag", + "name" : "tag", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ] + }, + "methods" : [ + { + "abiName" : "bjs_ConstructorDefaults_describe", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "describe", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + } + ], + "name" : "ConstructorDefaults", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "count", + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "enabled", + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "status", + "type" : { + "caseEnum" : { + "_0" : "Status" + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "tag", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "swiftCallName" : "ConstructorDefaults" + } + ], + "enums" : [ + { + "cases" : [ + { + "associatedValues" : [ + + ], + "name" : "active" + }, + { + "associatedValues" : [ + + ], + "name" : "inactive" + }, + { + "associatedValues" : [ + + ], + "name" : "pending" + } + ], + "emitStyle" : "const", + "explicitAccessControl" : "public", + "name" : "Status", + "staticMethods" : [ + + ], + "staticProperties" : [ + + ], + "swiftCallName" : "Status" + } + ], + "functions" : [ + { + "abiName" : "bjs_testStringDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testStringDefault", + "parameters" : [ + { + "defaultValue" : { + "string" : { + "_0" : "Hello World" + } + }, + "label" : "message", + "name" : "message", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_testNegativeIntDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testNegativeIntDefault", + "parameters" : [ + { + "defaultValue" : { + "int" : { + "_0" : -42 + } + }, + "label" : "value", + "name" : "value", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_testBoolDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testBoolDefault", + "parameters" : [ + { + "defaultValue" : { + "bool" : { + "_0" : true + } + }, + "label" : "flag", + "name" : "flag", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "abiName" : "bjs_testNegativeFloatDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testNegativeFloatDefault", + "parameters" : [ + { + "defaultValue" : { + "float" : { + "_0" : -273.15 + } + }, + "label" : "temp", + "name" : "temp", + "type" : { + "float" : { + + } + } + } + ], + "returnType" : { + "float" : { + + } + } + }, + { + "abiName" : "bjs_testDoubleDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testDoubleDefault", + "parameters" : [ + { + "defaultValue" : { + "double" : { + "_0" : 2.718 + } + }, + "label" : "precision", + "name" : "precision", + "type" : { + "double" : { + + } + } + } + ], + "returnType" : { + "double" : { + + } + } + }, + { + "abiName" : "bjs_testOptionalDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testOptionalDefault", + "parameters" : [ + { + "defaultValue" : { + "null" : { + + } + }, + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_testOptionalStringDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testOptionalStringDefault", + "parameters" : [ + { + "defaultValue" : { + "string" : { + "_0" : "Hi" + } + }, + "label" : "greeting", + "name" : "greeting", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_testMultipleDefaults", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testMultipleDefaults", + "parameters" : [ + { + "defaultValue" : { + "string" : { + "_0" : "Default Title" + } + }, + "label" : "title", + "name" : "title", + "type" : { + "string" : { + + } + } + }, + { + "defaultValue" : { + "int" : { + "_0" : 10 + } + }, + "label" : "count", + "name" : "count", + "type" : { + "int" : { + + } + } + }, + { + "defaultValue" : { + "bool" : { + "_0" : false + } + }, + "label" : "enabled", + "name" : "enabled", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_testEnumDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testEnumDefault", + "parameters" : [ + { + "defaultValue" : { + "enumCase" : { + "_0" : "Status", + "_1" : "active" + } + }, + "label" : "status", + "name" : "status", + "type" : { + "caseEnum" : { + "_0" : "Status" + } + } + } + ], + "returnType" : { + "caseEnum" : { + "_0" : "Status" + } + } + }, + { + "abiName" : "bjs_testComplexInit", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testComplexInit", + "parameters" : [ + { + "defaultValue" : { + "objectWithArguments" : { + "_0" : "DefaultGreeter", + "_1" : [ + { + "string" : { + "_0" : "DefaultUser" + } + } + ] + } + }, + "label" : "greeter", + "name" : "greeter", + "type" : { + "swiftHeapObject" : { + "_0" : "DefaultGreeter" + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "DefaultGreeter" + } + } + }, + { + "abiName" : "bjs_testEmptyInit", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testEmptyInit", + "parameters" : [ + { + "defaultValue" : { + "object" : { + "_0" : "EmptyGreeter" + } + }, + "label" : "greeter", + "name" : "greeter", + "type" : { + "swiftHeapObject" : { + "_0" : "EmptyGreeter" + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "EmptyGreeter" + } + } + } + ], + "moduleName" : "TestModule" +} \ No newline at end of file diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/DefaultParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/DefaultParameters.swift new file mode 100644 index 00000000..fd382c0a --- /dev/null +++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/DefaultParameters.swift @@ -0,0 +1,397 @@ +// NOTICE: This is auto-generated code by BridgeJS from JavaScriptKit, +// DO NOT EDIT. +// +// To update this file, just rebuild your project or run +// `swift package bridge-js`. + +@_spi(BridgeJS) import JavaScriptKit + +extension Status: _BridgedSwiftCaseEnum { + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerParameter() -> Int32 { + return bridgeJSRawValue + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftReturn(_ value: Int32) -> Status { + return Status(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public static func bridgeJSLiftParameter(_ value: Int32) -> Status { + return Status(bridgeJSRawValue: value)! + } + @_spi(BridgeJS) @_transparent public consuming func bridgeJSLowerReturn() -> Int32 { + return bridgeJSRawValue + } + + private init?(bridgeJSRawValue: Int32) { + switch bridgeJSRawValue { + case 0: + self = .active + case 1: + self = .inactive + case 2: + self = .pending + default: + return nil + } + } + + private var bridgeJSRawValue: Int32 { + switch self { + case .active: + return 0 + case .inactive: + return 1 + case .pending: + return 2 + } + } +} + +@_expose(wasm, "bjs_testStringDefault") +@_cdecl("bjs_testStringDefault") +public func _bjs_testStringDefault(messageBytes: Int32, messageLength: Int32) -> Void { + #if arch(wasm32) + let ret = testStringDefault(message: String.bridgeJSLiftParameter(messageBytes, messageLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testNegativeIntDefault") +@_cdecl("bjs_testNegativeIntDefault") +public func _bjs_testNegativeIntDefault(value: Int32) -> Int32 { + #if arch(wasm32) + let ret = testNegativeIntDefault(value: Int.bridgeJSLiftParameter(value)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testBoolDefault") +@_cdecl("bjs_testBoolDefault") +public func _bjs_testBoolDefault(flag: Int32) -> Int32 { + #if arch(wasm32) + let ret = testBoolDefault(flag: Bool.bridgeJSLiftParameter(flag)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testNegativeFloatDefault") +@_cdecl("bjs_testNegativeFloatDefault") +public func _bjs_testNegativeFloatDefault(temp: Float32) -> Float32 { + #if arch(wasm32) + let ret = testNegativeFloatDefault(temp: Float.bridgeJSLiftParameter(temp)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testDoubleDefault") +@_cdecl("bjs_testDoubleDefault") +public func _bjs_testDoubleDefault(precision: Float64) -> Float64 { + #if arch(wasm32) + let ret = testDoubleDefault(precision: Double.bridgeJSLiftParameter(precision)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testOptionalDefault") +@_cdecl("bjs_testOptionalDefault") +public func _bjs_testOptionalDefault(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = testOptionalDefault(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testOptionalStringDefault") +@_cdecl("bjs_testOptionalStringDefault") +public func _bjs_testOptionalStringDefault(greetingIsSome: Int32, greetingBytes: Int32, greetingLength: Int32) -> Void { + #if arch(wasm32) + let ret = testOptionalStringDefault(greeting: Optional.bridgeJSLiftParameter(greetingIsSome, greetingBytes, greetingLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testMultipleDefaults") +@_cdecl("bjs_testMultipleDefaults") +public func _bjs_testMultipleDefaults(titleBytes: Int32, titleLength: Int32, count: Int32, enabled: Int32) -> Void { + #if arch(wasm32) + let ret = testMultipleDefaults(title: String.bridgeJSLiftParameter(titleBytes, titleLength), count: Int.bridgeJSLiftParameter(count), enabled: Bool.bridgeJSLiftParameter(enabled)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testEnumDefault") +@_cdecl("bjs_testEnumDefault") +public func _bjs_testEnumDefault(status: Int32) -> Int32 { + #if arch(wasm32) + let ret = testEnumDefault(status: Status.bridgeJSLiftParameter(status)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testComplexInit") +@_cdecl("bjs_testComplexInit") +public func _bjs_testComplexInit(greeter: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = testComplexInit(greeter: DefaultGreeter.bridgeJSLiftParameter(greeter)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testEmptyInit") +@_cdecl("bjs_testEmptyInit") +public func _bjs_testEmptyInit(greeter: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = testEmptyInit(greeter: EmptyGreeter.bridgeJSLiftParameter(greeter)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_DefaultGreeter_init") +@_cdecl("bjs_DefaultGreeter_init") +public func _bjs_DefaultGreeter_init(nameBytes: Int32, nameLength: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = DefaultGreeter(name: String.bridgeJSLiftParameter(nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_DefaultGreeter_name_get") +@_cdecl("bjs_DefaultGreeter_name_get") +public func _bjs_DefaultGreeter_name_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = DefaultGreeter.bridgeJSLiftParameter(_self).name + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_DefaultGreeter_name_set") +@_cdecl("bjs_DefaultGreeter_name_set") +public func _bjs_DefaultGreeter_name_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + DefaultGreeter.bridgeJSLiftParameter(_self).name = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_DefaultGreeter_deinit") +@_cdecl("bjs_DefaultGreeter_deinit") +public func _bjs_DefaultGreeter_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension DefaultGreeter: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "TestModule", name: "bjs_DefaultGreeter_wrap") + func _bjs_DefaultGreeter_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_DefaultGreeter_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_DefaultGreeter_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} + +@_expose(wasm, "bjs_EmptyGreeter_init") +@_cdecl("bjs_EmptyGreeter_init") +public func _bjs_EmptyGreeter_init() -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = EmptyGreeter() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_EmptyGreeter_deinit") +@_cdecl("bjs_EmptyGreeter_deinit") +public func _bjs_EmptyGreeter_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension EmptyGreeter: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "TestModule", name: "bjs_EmptyGreeter_wrap") + func _bjs_EmptyGreeter_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_EmptyGreeter_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_EmptyGreeter_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} + +@_expose(wasm, "bjs_ConstructorDefaults_init") +@_cdecl("bjs_ConstructorDefaults_init") +public func _bjs_ConstructorDefaults_init(nameBytes: Int32, nameLength: Int32, count: Int32, enabled: Int32, status: Int32, tagIsSome: Int32, tagBytes: Int32, tagLength: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = ConstructorDefaults(name: String.bridgeJSLiftParameter(nameBytes, nameLength), count: Int.bridgeJSLiftParameter(count), enabled: Bool.bridgeJSLiftParameter(enabled), status: Status.bridgeJSLiftParameter(status), tag: Optional.bridgeJSLiftParameter(tagIsSome, tagBytes, tagLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_describe") +@_cdecl("bjs_ConstructorDefaults_describe") +public func _bjs_ConstructorDefaults_describe(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).describe() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_name_get") +@_cdecl("bjs_ConstructorDefaults_name_get") +public func _bjs_ConstructorDefaults_name_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).name + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_name_set") +@_cdecl("bjs_ConstructorDefaults_name_set") +public func _bjs_ConstructorDefaults_name_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).name = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_count_get") +@_cdecl("bjs_ConstructorDefaults_count_get") +public func _bjs_ConstructorDefaults_count_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).count + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_count_set") +@_cdecl("bjs_ConstructorDefaults_count_set") +public func _bjs_ConstructorDefaults_count_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).count = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_enabled_get") +@_cdecl("bjs_ConstructorDefaults_enabled_get") +public func _bjs_ConstructorDefaults_enabled_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).enabled + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_enabled_set") +@_cdecl("bjs_ConstructorDefaults_enabled_set") +public func _bjs_ConstructorDefaults_enabled_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).enabled = Bool.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_status_get") +@_cdecl("bjs_ConstructorDefaults_status_get") +public func _bjs_ConstructorDefaults_status_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).status + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_status_set") +@_cdecl("bjs_ConstructorDefaults_status_set") +public func _bjs_ConstructorDefaults_status_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).status = Status.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_tag_get") +@_cdecl("bjs_ConstructorDefaults_tag_get") +public func _bjs_ConstructorDefaults_tag_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).tag + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_tag_set") +@_cdecl("bjs_ConstructorDefaults_tag_set") +public func _bjs_ConstructorDefaults_tag_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).tag = Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_deinit") +@_cdecl("bjs_ConstructorDefaults_deinit") +public func _bjs_ConstructorDefaults_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension ConstructorDefaults: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "TestModule", name: "bjs_ConstructorDefaults_wrap") + func _bjs_ConstructorDefaults_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_ConstructorDefaults_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_ConstructorDefaults_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} \ No newline at end of file diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md index 41315708..05be58fa 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift-to-JavaScript.md @@ -67,6 +67,7 @@ This command will: - - - +- - - - diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Default-Parameters.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Default-Parameters.md new file mode 100644 index 00000000..11574f0d --- /dev/null +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Default-Parameters.md @@ -0,0 +1,159 @@ +# Default Parameters in Exported Swift Functions + +Learn how to use default parameter values in Swift functions and constructors exported to JavaScript. + +## Overview + +> Tip: You can quickly preview what interfaces will be exposed on the Swift/TypeScript sides using the [BridgeJS Playground](https://swiftwasm.org/JavaScriptKit/PlayBridgeJS/). + +BridgeJS supports default parameter values for Swift functions and class constructors exported to JavaScript. When you specify default values in your Swift code, they are automatically applied in the generated JavaScript bindings. + +```swift +import JavaScriptKit + +@JS public func greet(name: String = "World", enthusiastic: Bool = false) -> String { + let greeting = "Hello, \(name)" + return enthusiastic ? "\(greeting)!" : greeting +} +``` + +In JavaScript, parameters with defaults become optional: + +```javascript +exports.greet(); // "Hello, World" +exports.greet("Alice"); // "Hello, Alice" +exports.greet("Bob", true); // "Hello, Bob!" +``` + +The generated TypeScript definitions show optional parameters with JSDoc comments: + +```typescript +export type Exports = { + /** + * @param name - Optional parameter (default: "World") + * @param enthusiastic - Optional parameter (default: false) + */ + greet(name?: string, enthusiastic?: boolean): string; +} +``` + +## Skipping Parameters With Default Values + +To use a default value for a middle parameter while providing later parameters, pass `undefined`: + +```swift +@JS public func configure(title: String = "Default", count: Int = -10, enabled: Bool = false) -> String { + return "\(title): \(count) (\(enabled))" +} +``` + +```javascript +// Use all defaults +exports.configure(); // "Default: 10 (false)" +exports.configure("Custom"); // "Custom: -10 (false)" +exports.configure("Custom", undefined, true); // "Custom: -10 (true)" +exports.configure("Custom", 5, true); // "Custom: 5 (true)" +``` + +## Default Parameters in Constructors + +Constructor parameters also support default values, making it easy to create instances with flexible initialization options: + +```swift +@JS class Config { + @JS var name: String + @JS var timeout: Int + @JS var retries: Int + + @JS init(name: String = "default", timeout: Int = 30, retries: Int = 3) { + self.name = name + self.timeout = timeout + self.retries = retries + } +} +``` + +In JavaScript, you can omit constructor parameters or use `undefined` to skip them: + +```javascript +const c1 = new exports.Config(); // name: "default", timeout: 30, retries: 3 +const c2 = new exports.Config("custom"); // name: "custom", timeout: 30, retries: 3 +const c3 = new exports.Config("api", 60); // name: "api", timeout: 60, retries: 3 +const c4 = new exports.Config("api", undefined, 5); // name: "api", timeout: 30, retries: 5 +``` + +The generated TypeScript definitions include JSDoc comments for constructor parameters: + +```typescript +/** + * @param name - Optional parameter (default: "default") + * @param timeout - Optional parameter (default: 30) + * @param retries - Optional parameter (default: 3) + */ +new(name?: string, timeout?: number, retries?: number): Config; +``` + +## Supported Default Value Types + +The following default value types are supported for both function and constructor parameters: + +| Default Value Type | Swift Example | JavaScript/TypeScript | +|:-------------------|:-------------|:----------------------| +| String literals | `"hello"` | `"hello"` | +| Integer literals | `42` | `42` | +| Float literals | `3.14` | `3.14` | +| Double literals | `2.718` | `2.718` | +| Boolean literals | `true`, `false` | `true`, `false` | +| Nil for optionals | `nil` | `null` | +| Enum cases (shorthand) | `.north` | `Direction.North` | +| Enum cases (qualified) | `Direction.north` | `Direction.North` | +| Object initialization (no args) | `MyClass()` | `new MyClass()` | +| Object initialization (literal args) | `MyClass("value", 42)` | `new MyClass("value", 42)` | + +## Working with Class Instances as Default Parameters + +You can use class initialization expressions as default values: + +```swift +@JS class Config { + var setting: String + + @JS init(setting: String) { + self.setting = setting + } +} + +@JS public func process(config: Config = Config(setting: "default")) -> String { + return "Using: \(config.setting)" +} +``` + +In JavaScript: + +```javascript +exports.process(); // "Using: default" + +const custom = new exports.Config("custom"); +exports.process(custom); // "Using: custom" +custom.release(); +``` + +**Limitations for object initialization:** +- Constructor arguments must be literal values (`"text"`, `42`, `true`, `false`, `nil`) +- Complex expressions in constructor arguments are not supported +- Computed properties or method calls as arguments are not supported + +## Unsupported Default Value Types + +The following expressions are **not supported** as default parameter values: + +| Expression Type | Example | Status | +|:----------------|:--------|:-------| +| Method calls | `Date().description` | ❌ | +| Closures | `{ "computed" }()` | ❌ | +| Array literals | `[1, 2, 3]` | ❌ | +| Dictionary literals | `["key": "value"]` | ❌ | +| Binary operations | `10 + 20` | ❌ | +| Complex member access | `Config.shared.value` | ❌ | +| Ternary operators | `flag ? "a" : "b"` | ❌ | +| Object init with complex args | `Config(setting: getValue())` | ❌ | diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Function.md b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Function.md index fff45f11..ebc8037c 100644 --- a/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Function.md +++ b/Sources/JavaScriptKit/Documentation.docc/Articles/BridgeJS/Exporting-Swift/Exporting-Swift-Function.md @@ -150,4 +150,4 @@ export type Exports = { | Async methods: `func x() async` | ✅ | | Generics | ❌ | | Opaque types: `func x() -> some P`, `func y(_: some P)` | ❌ | -| Default parameter values: `func x(_ foo: String = "")` | ❌ | \ No newline at end of file +| Default parameter values: `func x(_ foo: String = "")` | ✅ (See ) | \ No newline at end of file diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift index a2cd3c1a..a5959e14 100644 --- a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift +++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift @@ -700,6 +700,85 @@ enum APIOptionalResult { } } +// MARK: - Default Parameters + +@JS func testStringDefault(message: String = "Hello World") -> String { + return message +} + +@JS func testIntDefault(count: Int = 42) -> Int { + return count +} + +@JS func testBoolDefault(flag: Bool = true) -> Bool { + return flag +} + +@JS func testOptionalDefault(name: String? = nil) -> String? { + return name +} + +@JS func testMultipleDefaults( + title: String = "Default Title", + count: Int = -10, + enabled: Bool = false +) -> String { + return "\(title): \(count) (\(enabled))" +} + +@JS func testSimpleEnumDefault(status: Status = .success) -> Status { + return status +} + +@JS func testDirectionDefault(direction: Direction = .north) -> Direction { + return direction +} + +@JS func testRawStringEnumDefault(theme: Theme = .light) -> Theme { + return theme +} + +@JS func testComplexInit(greeter: Greeter = Greeter(name: "DefaultGreeter")) -> String { + return greeter.greet() +} + +@JS func testEmptyInit(_ object: StaticPropertyHolder = StaticPropertyHolder()) -> StaticPropertyHolder { + return object +} + +@JS class ConstructorDefaults { + @JS var name: String + @JS var count: Int + @JS var enabled: Bool + @JS var status: Status + @JS var tag: String? + + @JS init( + name: String = "Default", + count: Int = 42, + enabled: Bool = true, + status: Status = .success, + tag: String? = nil + ) { + self.name = name + self.count = count + self.enabled = enabled + self.status = status + self.tag = tag + } + + @JS func describe() -> String { + let tagStr = tag ?? "nil" + let statusStr: String + switch status { + case .loading: statusStr = "loading" + case .success: statusStr = "success" + case .error: statusStr = "error" + } + return "\(name):\(count):\(enabled):\(statusStr):\(tagStr)" + } +} + // MARK: - Static Properties @JS class StaticPropertyHolder { diff --git a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift index 3b5baabb..0eff4e0b 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift +++ b/Tests/BridgeJSRuntimeTests/Generated/BridgeJS.ExportSwift.swift @@ -1918,6 +1918,116 @@ public func _bjs_getObserverStats() -> Void { #endif } +@_expose(wasm, "bjs_testStringDefault") +@_cdecl("bjs_testStringDefault") +public func _bjs_testStringDefault(messageBytes: Int32, messageLength: Int32) -> Void { + #if arch(wasm32) + let ret = testStringDefault(message: String.bridgeJSLiftParameter(messageBytes, messageLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testIntDefault") +@_cdecl("bjs_testIntDefault") +public func _bjs_testIntDefault(count: Int32) -> Int32 { + #if arch(wasm32) + let ret = testIntDefault(count: Int.bridgeJSLiftParameter(count)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testBoolDefault") +@_cdecl("bjs_testBoolDefault") +public func _bjs_testBoolDefault(flag: Int32) -> Int32 { + #if arch(wasm32) + let ret = testBoolDefault(flag: Bool.bridgeJSLiftParameter(flag)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testOptionalDefault") +@_cdecl("bjs_testOptionalDefault") +public func _bjs_testOptionalDefault(nameIsSome: Int32, nameBytes: Int32, nameLength: Int32) -> Void { + #if arch(wasm32) + let ret = testOptionalDefault(name: Optional.bridgeJSLiftParameter(nameIsSome, nameBytes, nameLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testMultipleDefaults") +@_cdecl("bjs_testMultipleDefaults") +public func _bjs_testMultipleDefaults(titleBytes: Int32, titleLength: Int32, count: Int32, enabled: Int32) -> Void { + #if arch(wasm32) + let ret = testMultipleDefaults(title: String.bridgeJSLiftParameter(titleBytes, titleLength), count: Int.bridgeJSLiftParameter(count), enabled: Bool.bridgeJSLiftParameter(enabled)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testSimpleEnumDefault") +@_cdecl("bjs_testSimpleEnumDefault") +public func _bjs_testSimpleEnumDefault(status: Int32) -> Int32 { + #if arch(wasm32) + let ret = testSimpleEnumDefault(status: Status.bridgeJSLiftParameter(status)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testDirectionDefault") +@_cdecl("bjs_testDirectionDefault") +public func _bjs_testDirectionDefault(direction: Int32) -> Int32 { + #if arch(wasm32) + let ret = testDirectionDefault(direction: Direction.bridgeJSLiftParameter(direction)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testRawStringEnumDefault") +@_cdecl("bjs_testRawStringEnumDefault") +public func _bjs_testRawStringEnumDefault(themeBytes: Int32, themeLength: Int32) -> Void { + #if arch(wasm32) + let ret = testRawStringEnumDefault(theme: Theme.bridgeJSLiftParameter(themeBytes, themeLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testComplexInit") +@_cdecl("bjs_testComplexInit") +public func _bjs_testComplexInit(greeter: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = testComplexInit(greeter: Greeter.bridgeJSLiftParameter(greeter)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_testEmptyInit") +@_cdecl("bjs_testEmptyInit") +public func _bjs_testEmptyInit(object: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = testEmptyInit(_: StaticPropertyHolder.bridgeJSLiftParameter(object)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + @_expose(wasm, "bjs_getAllStaticPropertyValues") @_cdecl("bjs_getAllStaticPropertyValues") public func _bjs_getAllStaticPropertyValues() -> Void { @@ -2745,6 +2855,153 @@ extension MathUtils: ConvertibleToJSValue, _BridgedSwiftHeapObject { } } +@_expose(wasm, "bjs_ConstructorDefaults_init") +@_cdecl("bjs_ConstructorDefaults_init") +public func _bjs_ConstructorDefaults_init(nameBytes: Int32, nameLength: Int32, count: Int32, enabled: Int32, status: Int32, tagIsSome: Int32, tagBytes: Int32, tagLength: Int32) -> UnsafeMutableRawPointer { + #if arch(wasm32) + let ret = ConstructorDefaults(name: String.bridgeJSLiftParameter(nameBytes, nameLength), count: Int.bridgeJSLiftParameter(count), enabled: Bool.bridgeJSLiftParameter(enabled), status: Status.bridgeJSLiftParameter(status), tag: Optional.bridgeJSLiftParameter(tagIsSome, tagBytes, tagLength)) + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_describe") +@_cdecl("bjs_ConstructorDefaults_describe") +public func _bjs_ConstructorDefaults_describe(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).describe() + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_name_get") +@_cdecl("bjs_ConstructorDefaults_name_get") +public func _bjs_ConstructorDefaults_name_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).name + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_name_set") +@_cdecl("bjs_ConstructorDefaults_name_set") +public func _bjs_ConstructorDefaults_name_set(_self: UnsafeMutableRawPointer, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).name = String.bridgeJSLiftParameter(valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_count_get") +@_cdecl("bjs_ConstructorDefaults_count_get") +public func _bjs_ConstructorDefaults_count_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).count + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_count_set") +@_cdecl("bjs_ConstructorDefaults_count_set") +public func _bjs_ConstructorDefaults_count_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).count = Int.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_enabled_get") +@_cdecl("bjs_ConstructorDefaults_enabled_get") +public func _bjs_ConstructorDefaults_enabled_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).enabled + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_enabled_set") +@_cdecl("bjs_ConstructorDefaults_enabled_set") +public func _bjs_ConstructorDefaults_enabled_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).enabled = Bool.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_status_get") +@_cdecl("bjs_ConstructorDefaults_status_get") +public func _bjs_ConstructorDefaults_status_get(_self: UnsafeMutableRawPointer) -> Int32 { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).status + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_status_set") +@_cdecl("bjs_ConstructorDefaults_status_set") +public func _bjs_ConstructorDefaults_status_set(_self: UnsafeMutableRawPointer, value: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).status = Status.bridgeJSLiftParameter(value) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_tag_get") +@_cdecl("bjs_ConstructorDefaults_tag_get") +public func _bjs_ConstructorDefaults_tag_get(_self: UnsafeMutableRawPointer) -> Void { + #if arch(wasm32) + let ret = ConstructorDefaults.bridgeJSLiftParameter(_self).tag + return ret.bridgeJSLowerReturn() + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_tag_set") +@_cdecl("bjs_ConstructorDefaults_tag_set") +public func _bjs_ConstructorDefaults_tag_set(_self: UnsafeMutableRawPointer, valueIsSome: Int32, valueBytes: Int32, valueLength: Int32) -> Void { + #if arch(wasm32) + ConstructorDefaults.bridgeJSLiftParameter(_self).tag = Optional.bridgeJSLiftParameter(valueIsSome, valueBytes, valueLength) + #else + fatalError("Only available on WebAssembly") + #endif +} + +@_expose(wasm, "bjs_ConstructorDefaults_deinit") +@_cdecl("bjs_ConstructorDefaults_deinit") +public func _bjs_ConstructorDefaults_deinit(pointer: UnsafeMutableRawPointer) { + Unmanaged.fromOpaque(pointer).release() +} + +extension ConstructorDefaults: ConvertibleToJSValue, _BridgedSwiftHeapObject { + var jsValue: JSValue { + #if arch(wasm32) + @_extern(wasm, module: "BridgeJSRuntimeTests", name: "bjs_ConstructorDefaults_wrap") + func _bjs_ConstructorDefaults_wrap(_: UnsafeMutableRawPointer) -> Int32 + #else + func _bjs_ConstructorDefaults_wrap(_: UnsafeMutableRawPointer) -> Int32 { + fatalError("Only available on WebAssembly") + } + #endif + return .object(JSObject(id: UInt32(bitPattern: _bjs_ConstructorDefaults_wrap(Unmanaged.passRetained(self).toOpaque())))) + } +} + @_expose(wasm, "bjs_StaticPropertyHolder_init") @_cdecl("bjs_StaticPropertyHolder_init") public func _bjs_StaticPropertyHolder_init() -> UnsafeMutableRawPointer { diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json index 15c5a3a7..069b4332 100644 --- a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json +++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/BridgeJS.ExportSwift.json @@ -806,6 +806,170 @@ ], "swiftCallName" : "MathUtils" }, + { + "constructor" : { + "abiName" : "bjs_ConstructorDefaults_init", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "parameters" : [ + { + "defaultValue" : { + "string" : { + "_0" : "Default" + } + }, + "label" : "name", + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "defaultValue" : { + "int" : { + "_0" : 42 + } + }, + "label" : "count", + "name" : "count", + "type" : { + "int" : { + + } + } + }, + { + "defaultValue" : { + "bool" : { + "_0" : true + } + }, + "label" : "enabled", + "name" : "enabled", + "type" : { + "bool" : { + + } + } + }, + { + "defaultValue" : { + "enumCase" : { + "_0" : "Status", + "_1" : "success" + } + }, + "label" : "status", + "name" : "status", + "type" : { + "caseEnum" : { + "_0" : "Status" + } + } + }, + { + "defaultValue" : { + "null" : { + + } + }, + "label" : "tag", + "name" : "tag", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ] + }, + "methods" : [ + { + "abiName" : "bjs_ConstructorDefaults_describe", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "describe", + "parameters" : [ + + ], + "returnType" : { + "string" : { + + } + } + } + ], + "name" : "ConstructorDefaults", + "properties" : [ + { + "isReadonly" : false, + "isStatic" : false, + "name" : "name", + "type" : { + "string" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "count", + "type" : { + "int" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "enabled", + "type" : { + "bool" : { + + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "status", + "type" : { + "caseEnum" : { + "_0" : "Status" + } + } + }, + { + "isReadonly" : false, + "isStatic" : false, + "name" : "tag", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "swiftCallName" : "ConstructorDefaults" + }, { "constructor" : { "abiName" : "bjs_StaticPropertyHolder_init", @@ -4898,6 +5062,354 @@ } } }, + { + "abiName" : "bjs_testStringDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testStringDefault", + "parameters" : [ + { + "defaultValue" : { + "string" : { + "_0" : "Hello World" + } + }, + "label" : "message", + "name" : "message", + "type" : { + "string" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_testIntDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testIntDefault", + "parameters" : [ + { + "defaultValue" : { + "int" : { + "_0" : 42 + } + }, + "label" : "count", + "name" : "count", + "type" : { + "int" : { + + } + } + } + ], + "returnType" : { + "int" : { + + } + } + }, + { + "abiName" : "bjs_testBoolDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testBoolDefault", + "parameters" : [ + { + "defaultValue" : { + "bool" : { + "_0" : true + } + }, + "label" : "flag", + "name" : "flag", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "bool" : { + + } + } + }, + { + "abiName" : "bjs_testOptionalDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testOptionalDefault", + "parameters" : [ + { + "defaultValue" : { + "null" : { + + } + }, + "label" : "name", + "name" : "name", + "type" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + } + ], + "returnType" : { + "optional" : { + "_0" : { + "string" : { + + } + } + } + } + }, + { + "abiName" : "bjs_testMultipleDefaults", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testMultipleDefaults", + "parameters" : [ + { + "defaultValue" : { + "string" : { + "_0" : "Default Title" + } + }, + "label" : "title", + "name" : "title", + "type" : { + "string" : { + + } + } + }, + { + "defaultValue" : { + "int" : { + "_0" : -10 + } + }, + "label" : "count", + "name" : "count", + "type" : { + "int" : { + + } + } + }, + { + "defaultValue" : { + "bool" : { + "_0" : false + } + }, + "label" : "enabled", + "name" : "enabled", + "type" : { + "bool" : { + + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_testSimpleEnumDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testSimpleEnumDefault", + "parameters" : [ + { + "defaultValue" : { + "enumCase" : { + "_0" : "Status", + "_1" : "success" + } + }, + "label" : "status", + "name" : "status", + "type" : { + "caseEnum" : { + "_0" : "Status" + } + } + } + ], + "returnType" : { + "caseEnum" : { + "_0" : "Status" + } + } + }, + { + "abiName" : "bjs_testDirectionDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testDirectionDefault", + "parameters" : [ + { + "defaultValue" : { + "enumCase" : { + "_0" : "Direction", + "_1" : "north" + } + }, + "label" : "direction", + "name" : "direction", + "type" : { + "caseEnum" : { + "_0" : "Direction" + } + } + } + ], + "returnType" : { + "caseEnum" : { + "_0" : "Direction" + } + } + }, + { + "abiName" : "bjs_testRawStringEnumDefault", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testRawStringEnumDefault", + "parameters" : [ + { + "defaultValue" : { + "enumCase" : { + "_0" : "Theme", + "_1" : "light" + } + }, + "label" : "theme", + "name" : "theme", + "type" : { + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" + } + } + } + ], + "returnType" : { + "rawValueEnum" : { + "_0" : "Theme", + "_1" : "String" + } + } + }, + { + "abiName" : "bjs_testComplexInit", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testComplexInit", + "parameters" : [ + { + "defaultValue" : { + "objectWithArguments" : { + "_0" : "Greeter", + "_1" : [ + { + "string" : { + "_0" : "DefaultGreeter" + } + } + ] + } + }, + "label" : "greeter", + "name" : "greeter", + "type" : { + "swiftHeapObject" : { + "_0" : "Greeter" + } + } + } + ], + "returnType" : { + "string" : { + + } + } + }, + { + "abiName" : "bjs_testEmptyInit", + "effects" : { + "isAsync" : false, + "isStatic" : false, + "isThrows" : false + }, + "name" : "testEmptyInit", + "parameters" : [ + { + "defaultValue" : { + "object" : { + "_0" : "StaticPropertyHolder" + } + }, + "label" : "_", + "name" : "object", + "type" : { + "swiftHeapObject" : { + "_0" : "StaticPropertyHolder" + } + } + } + ], + "returnType" : { + "swiftHeapObject" : { + "_0" : "StaticPropertyHolder" + } + } + }, { "abiName" : "bjs_getAllStaticPropertyValues", "effects" : { diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs index 6b877d3a..32560659 100644 --- a/Tests/prelude.mjs +++ b/Tests/prelude.mjs @@ -642,6 +642,67 @@ function BridgeJSRuntimeTests_runJsWorks(instance, exports) { assert.equal(StaticCalculatorValues.Scientific, exports.StaticCalculator.Scientific); assert.equal(StaticCalculatorValues.Basic, exports.StaticCalculator.Basic); assert.equal(globalThis.StaticUtils.Nested.roundtrip("hello world"), "hello world"); + + // Test default parameters + assert.equal(exports.testStringDefault(), "Hello World"); + assert.equal(exports.testStringDefault("Custom Message"), "Custom Message"); + + assert.equal(exports.testIntDefault(), 42); + assert.equal(exports.testIntDefault(100), 100); + + assert.equal(exports.testBoolDefault(), true); + assert.equal(exports.testBoolDefault(false), false); + + assert.equal(exports.testOptionalDefault(), null); + assert.equal(exports.testOptionalDefault("Test"), "Test"); + + assert.equal(exports.testMultipleDefaults(), "Default Title: -10 (false)"); + assert.equal(exports.testMultipleDefaults("Custom"), "Custom: -10 (false)"); + assert.equal(exports.testMultipleDefaults("Custom", 5), "Custom: 5 (false)"); + assert.equal(exports.testMultipleDefaults("Custom", undefined, true), "Custom: -10 (true)"); + assert.equal(exports.testMultipleDefaults("Custom", 5, true), "Custom: 5 (true)"); + + assert.equal(exports.testSimpleEnumDefault(), exports.Status.Success); + assert.equal(exports.testSimpleEnumDefault(exports.Status.Loading), exports.Status.Loading); + + assert.equal(exports.testDirectionDefault(), exports.Direction.North); + assert.equal(exports.testDirectionDefault(exports.Direction.South), exports.Direction.South); + + assert.equal(exports.testRawStringEnumDefault(), exports.Theme.Light); + assert.equal(exports.testRawStringEnumDefault(exports.Theme.Dark), exports.Theme.Dark); + + const holder = exports.testEmptyInit() + assert.notEqual(holder, null); + holder.release(); + + const customHolder = new exports.StaticPropertyHolder(); + assert.deepEqual(exports.testEmptyInit(customHolder), customHolder); + customHolder.release(); + + assert.equal(exports.testComplexInit(), "Hello, DefaultGreeter!"); + const customGreeter = new exports.Greeter("CustomName"); + assert.equal(exports.testComplexInit(customGreeter), "Hello, CustomName!"); + customGreeter.release(); + + const cd1 = new exports.ConstructorDefaults(); + assert.equal(cd1.describe(), "Default:42:true:success:nil"); + cd1.release(); + + const cd2 = new exports.ConstructorDefaults("Custom"); + assert.equal(cd2.describe(), "Custom:42:true:success:nil"); + cd2.release(); + + const cd3 = new exports.ConstructorDefaults("Custom", 100); + assert.equal(cd3.describe(), "Custom:100:true:success:nil"); + cd3.release(); + + const cd4 = new exports.ConstructorDefaults("Custom", undefined, false); + assert.equal(cd4.describe(), "Custom:42:false:success:nil"); + cd4.release(); + + const cd5 = new exports.ConstructorDefaults("Test", 99, false, exports.Status.Loading); + assert.equal(cd5.describe(), "Test:99:false:loading:nil"); + cd5.release(); } /** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge-js.d.ts').Exports} exports */