Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -502,7 +502,8 @@
"staticProperties" : [

],
"swiftCallName" : "APIResult"
"swiftCallName" : "APIResult",
"tsFullPath" : "APIResult"
},
{
"cases" : [
Expand Down Expand Up @@ -698,9 +699,11 @@
"staticProperties" : [

],
"swiftCallName" : "ComplexResult"
"swiftCallName" : "ComplexResult",
"tsFullPath" : "ComplexResult"
}
],
"exposeToGlobal" : false,
"functions" : [
{
"abiName" : "bjs_run",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,7 @@
"enums" : [

],
"exposeToGlobal" : false,
"functions" : [

],
Expand Down
2 changes: 1 addition & 1 deletion Examples/PlayBridgeJS/Sources/PlayBridgeJS/main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ import class Foundation.JSONDecoder
}

func _update(swiftSource: String, dtsSource: String) throws -> PlayBridgeJSOutput {
let exportSwift = ExportSwift(progress: .silent, moduleName: "Playground")
let exportSwift = ExportSwift(progress: .silent, moduleName: "Playground", exposeToGlobal: false)
let sourceFile = Parser.parse(source: swiftSource)
try exportSwift.addSourceFile(sourceFile, "Playground.swift")
let exportResult = try exportSwift.finalize()
Expand Down
13 changes: 13 additions & 0 deletions Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -180,5 +180,18 @@ let package = Package(
],
linkerSettings: testingLinkerFlags
),
.testTarget(
name: "BridgeJSGlobalTests",
dependencies: ["JavaScriptKit", "JavaScriptEventLoop"],
exclude: [
"bridge-js.config.json",
"bridge-js.d.ts",
"Generated/JavaScript",
],
swiftSettings: [
.enableExperimentalFeature("Extern")
],
linkerSettings: testingLinkerFlags
),
]
)
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,8 @@ struct BridgeJSBuildPlugin: BuildToolPlugin {
"export",
"--module-name",
target.name,
"--target-dir",
target.directoryURL.path,
"--output-skeleton",
outputSkeletonPath.path,
"--output-swift",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,8 @@ extension BridgeJSCommandPlugin.Context {
"export",
"--module-name",
target.name,
"--target-dir",
target.directoryURL.path,
"--output-skeleton",
generatedJavaScriptDirectory.appending(path: "BridgeJS.ExportSwift.json").path,
"--output-swift",
Expand Down
28 changes: 27 additions & 1 deletion Plugins/BridgeJS/Sources/BridgeJSCore/BridgeJSConfig.swift
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,31 @@ public struct BridgeJSConfig: Codable {
/// If not present, the tool will be searched for in the system PATH.
public var tools: [String: String]?

/// Whether to expose exported Swift APIs to the global namespace.
///
/// When `true`, exported functions, classes, and namespaces are available
/// via `globalThis` in JavaScript. When `false`, they are only available
/// through the exports object returned by `createExports()`.
///
/// Default: `false`
public var exposeToGlobal: Bool

public init(tools: [String: String]? = nil, exposeToGlobal: Bool = false) {
self.tools = tools
self.exposeToGlobal = exposeToGlobal
}

enum CodingKeys: String, CodingKey {
case tools
case exposeToGlobal
}

public init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
tools = try container.decodeIfPresent([String: String].self, forKey: .tools)
exposeToGlobal = try container.decodeIfPresent(Bool.self, forKey: .exposeToGlobal) ?? false
}

/// Load the configuration file from the SwiftPM package target directory.
///
/// Files are loaded **in this order** and merged (later files override earlier ones):
Expand Down Expand Up @@ -49,7 +74,8 @@ public struct BridgeJSConfig: Codable {
/// Merge the current configuration with the overrides.
func merging(overrides: BridgeJSConfig) -> BridgeJSConfig {
return BridgeJSConfig(
tools: (tools ?? [:]).merging(overrides.tools ?? [:], uniquingKeysWith: { $1 })
tools: (tools ?? [:]).merging(overrides.tools ?? [:], uniquingKeysWith: { $1 }),
exposeToGlobal: overrides.exposeToGlobal
)
}
}
18 changes: 15 additions & 3 deletions Plugins/BridgeJS/Sources/BridgeJSCore/ExportSwift.swift
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import BridgeJSUtilities
public class ExportSwift {
let progress: ProgressReporting
let moduleName: String

private let exposeToGlobal: Bool
private var exportedFunctions: [ExportedFunction] = []
private var exportedClasses: [ExportedClass] = []
private var exportedEnums: [ExportedEnum] = []
Expand All @@ -30,9 +30,10 @@ public class ExportSwift {
private let enumCodegen: EnumCodegen = EnumCodegen()
private let closureCodegen = ClosureCodegen()

public init(progress: ProgressReporting, moduleName: String) {
public init(progress: ProgressReporting, moduleName: String, exposeToGlobal: Bool) {
self.progress = progress
self.moduleName = moduleName
self.exposeToGlobal = exposeToGlobal
}

/// Processes a Swift source file to find declarations marked with @JS
Expand All @@ -55,6 +56,8 @@ public class ExportSwift {

/// Finalizes the export process and generates the bridge code
///
/// - Parameters:
/// - exposeToGlobal: Whether to expose exported APIs to the global namespace (default: false)
/// - Returns: A tuple containing the generated Swift code and a skeleton
/// describing the exported APIs
public func finalize() throws -> (outputSwift: String, outputSkeleton: ExportedSkeleton)? {
Expand All @@ -68,7 +71,8 @@ public class ExportSwift {
functions: exportedFunctions,
classes: exportedClasses,
enums: exportedEnums,
protocols: exportedProtocols
protocols: exportedProtocols,
exposeToGlobal: exposeToGlobal
)
)
}
Expand Down Expand Up @@ -882,10 +886,18 @@ public class ExportSwift {
message: "Enum visibility must be at least internal"
)

let tsFullPath: String
if let namespace = namespaceResult.namespace, !namespace.isEmpty {
tsFullPath = namespace.joined(separator: ".") + "." + name
} else {
tsFullPath = name
}

// Create enum directly in dictionary
let exportedEnum = ExportedEnum(
name: name,
swiftCallName: swiftCallName,
tsFullPath: tsFullPath,
explicitAccessControl: explicitAccessControl,
cases: [], // Will be populated in visit(EnumCaseDeclSyntax)
rawType: SwiftEnumRawType(rawType),
Expand Down
Loading