Skip to content

[BridgeJS] Support @JS var declarations for global scope imports #466

Closed

Description

@kateinoigakukun

Motivation

Currently, importing JavaScript APIs from the global scope requires users to write TypeScript declaration files and import APIs through the standard BridgeJS workflow. For simple use cases where users only need to access a few well-known global APIs like console.log() or document.getElementById(), this approach can feel heavyweight and requires additional setup steps.

Proposed Solution

Allow users to declare global scope imports directly in Swift using @JS var declarations with protocol interfaces, eliminating the need for separate .d.ts files for simple global API access.

High-level API

@JS protocol JSConsole {
    func log(_ message: String) throws(JSException)
    func error(_ message: String) throws(JSException)
}

@JSGetter(from: .global) var console: JSConsole

@JS class JSElement {
    var innerHTML: String { get set }
    func addEventListener(_ event: String, _ handler: @escaping () -> Void) throws(JSException)
}

@JS protocol JSDocument {
    func getElementById(_ id: String) throws(JSException) -> JSElement?
}

@JS(from: .global, jsName: "document") var jsDocument: JSDocument

@JSImport(from: .module("uuid"), jsName: "v4")
func uuidv4() throws(JSException) -> String

@JSImport(from: .module("./custom-component.js"), jsName: "defineCustomComponents")
func jsDefineCustomComponents() throws(JSException)

@JS func run() {
    print(uuidv4())
    jsDefineCustomComponents()
}

Implementation

This feature would require adding a macro plugin to support @JS var [name]: [Type] declarations that generates getter/setter accessors interacting with the code generated by @JS protocol

If it works well, we might be able to make ImportTS just to generate @JS annotated Swift code and centralize ABI-facing code in ExportSwift

Activity

  1. kateinoigakukun commented on Nov 9, 2025

    @kateinoigakukun
    MemberAuthor

    @krodak I'd like to hear your thoughts on this idea

  2. scottmarchant commented on Nov 18, 2025

    @scottmarchant
    Contributor

    @kateinoigakukun I love this idea! The only thing I would suggest is that we pick a different macro name for importing JS to Swift, so that it is clear which macros usages are bridging Swift to JS, and which ones are bridging JS to Swift. That also might help with the implementation under the hood as well, since the two different directions are opposite use cases.

    Not sure what the name should be. And wondering if it would make sense to use two different names for each use case if we go this route. Maybe @JSImport for bringing JS to Swift, and either @JS or @JSExport for Swift to JS?

    If we use the same @JS for both directions, the only thing that cues developers (and the implementation under the hood) of the difference in direction is the type on the right hand side (JSConsole, JSElement, etc). Otherwise, this usage looks a lot like an export if one is used to the other use case of exporting Swift to JS.

  3. scottmarchant commented on Nov 18, 2025

    @scottmarchant
    Contributor

    This would provide a cleaner API for exposing JS to Swift. If it helps prioritize, we're already able to achieve this using the lower-level existing constructs in JavaScriptKit for now. Happy to adopt this once someone has capacity to implement this.

  4. kateinoigakukun commented on Nov 19, 2025

    @kateinoigakukun
    MemberAuthor

    @scottmarchant I think having different names for them makes a lot of sense. It will make it easier for documentation too IMO

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions