diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 35405eaf..ffb7fefb 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -52,6 +52,7 @@ jobs:
make regenerate_swiftpm_resources
git diff --exit-code Sources/JavaScriptKit/Runtime
- run: swift test --package-path ./Plugins/PackageToJS
+ - run: swift test --package-path ./Plugins/BridgeJS
native-build:
# Check native build to make it easy to develop applications by Xcode
diff --git a/.gitignore b/.gitignore
index 232ea114..5aac0048 100644
--- a/.gitignore
+++ b/.gitignore
@@ -10,3 +10,4 @@ xcuserdata/
Examples/*/Bundle
Examples/*/package-lock.json
Package.resolved
+Plugins/BridgeJS/Sources/JavaScript/package-lock.json
diff --git a/Examples/ExportSwift/Package.swift b/Examples/ExportSwift/Package.swift
new file mode 100644
index 00000000..191278fd
--- /dev/null
+++ b/Examples/ExportSwift/Package.swift
@@ -0,0 +1,25 @@
+// swift-tools-version:6.0
+
+import PackageDescription
+
+let package = Package(
+ name: "MyApp",
+ platforms: [
+ .macOS(.v14)
+ ],
+ dependencies: [.package(name: "JavaScriptKit", path: "../../")],
+ targets: [
+ .executableTarget(
+ name: "MyApp",
+ dependencies: [
+ "JavaScriptKit"
+ ],
+ swiftSettings: [
+ .enableExperimentalFeature("Extern")
+ ],
+ plugins: [
+ .plugin(name: "BridgeJS", package: "JavaScriptKit")
+ ]
+ )
+ ]
+)
diff --git a/Examples/ExportSwift/Sources/main.swift b/Examples/ExportSwift/Sources/main.swift
new file mode 100644
index 00000000..44915521
--- /dev/null
+++ b/Examples/ExportSwift/Sources/main.swift
@@ -0,0 +1,34 @@
+import JavaScriptKit
+
+// Mark functions you want to export to JavaScript with the @JS attribute
+// This function will be available as `renderCircleSVG(size)` in JavaScript
+@JS public func renderCircleSVG(size: Int) -> String {
+ let strokeWidth = 3
+ let strokeColor = "black"
+ let fillColor = "red"
+ let cx = size / 2
+ let cy = size / 2
+ let r = (size / 2) - strokeWidth
+ var svg = ""
+ return svg
+}
+
+// Classes can also be exported using the @JS attribute
+// This class will be available as a constructor in JavaScript: new Greeter("name")
+@JS class Greeter {
+ var name: String
+
+ // Use @JS for initializers you want to expose
+ @JS init(name: String) {
+ self.name = name
+ }
+
+ // Methods need the @JS attribute to be accessible from JavaScript
+ // This method will be available as greeter.greet() in JavaScript
+ @JS public func greet() -> String {
+ "Hello, \(name)!"
+ }
+}
diff --git a/Examples/ExportSwift/index.html b/Examples/ExportSwift/index.html
new file mode 100644
index 00000000..ef3d190a
--- /dev/null
+++ b/Examples/ExportSwift/index.html
@@ -0,0 +1,12 @@
+
+
+
+
+ Getting Started
+
+
+
+
+
+
+
diff --git a/Examples/ExportSwift/index.js b/Examples/ExportSwift/index.js
new file mode 100644
index 00000000..4c5576b2
--- /dev/null
+++ b/Examples/ExportSwift/index.js
@@ -0,0 +1,14 @@
+import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
+const { exports } = await init({});
+
+const Greeter = exports.Greeter;
+const greeter = new Greeter("World");
+const circle = exports.renderCircleSVG(100);
+
+// Display the results
+const textOutput = document.createElement("div");
+textOutput.innerText = greeter.greet()
+document.body.appendChild(textOutput);
+const circleOutput = document.createElement("div");
+circleOutput.innerHTML = circle;
+document.body.appendChild(circleOutput);
diff --git a/Examples/ImportTS/Package.swift b/Examples/ImportTS/Package.swift
new file mode 100644
index 00000000..4809ec00
--- /dev/null
+++ b/Examples/ImportTS/Package.swift
@@ -0,0 +1,29 @@
+// swift-tools-version:6.0
+
+import PackageDescription
+
+let package = Package(
+ name: "MyApp",
+ platforms: [
+ .macOS(.v10_15),
+ .iOS(.v13),
+ .tvOS(.v13),
+ .watchOS(.v6),
+ .macCatalyst(.v13),
+ ],
+ dependencies: [.package(name: "JavaScriptKit", path: "../../")],
+ targets: [
+ .executableTarget(
+ name: "MyApp",
+ dependencies: [
+ "JavaScriptKit"
+ ],
+ swiftSettings: [
+ .enableExperimentalFeature("Extern")
+ ],
+ plugins: [
+ .plugin(name: "BridgeJS", package: "JavaScriptKit")
+ ]
+ )
+ ]
+)
diff --git a/Examples/ImportTS/Sources/bridge.d.ts b/Examples/ImportTS/Sources/bridge.d.ts
new file mode 100644
index 00000000..856bba9c
--- /dev/null
+++ b/Examples/ImportTS/Sources/bridge.d.ts
@@ -0,0 +1,24 @@
+// Function definition to expose console.log to Swift
+// Will be imported as a Swift function: consoleLog(message: String)
+export function consoleLog(message: string): void
+
+// TypeScript interface types are converted to Swift structs
+// This defines a subset of the browser's HTMLElement interface
+type HTMLElement = Pick & {
+ // Methods with object parameters are properly handled
+ appendChild(child: HTMLElement): void
+}
+
+// TypeScript object type with read-only properties
+// Properties will become Swift properties with appropriate access level
+type Document = {
+ // Regular property - will be read/write in Swift
+ title: string
+ // Read-only property - will be read-only in Swift
+ readonly body: HTMLElement
+ // Method returning an object - will become a Swift method returning an HTMLElement
+ createElement(tagName: string): HTMLElement
+}
+// Function returning a complex object
+// Will be imported as a Swift function: getDocument() -> Document
+export function getDocument(): Document
diff --git a/Examples/ImportTS/Sources/main.swift b/Examples/ImportTS/Sources/main.swift
new file mode 100644
index 00000000..4328b0a3
--- /dev/null
+++ b/Examples/ImportTS/Sources/main.swift
@@ -0,0 +1,26 @@
+import JavaScriptKit
+
+// This function is automatically generated by the @JS plugin
+// It demonstrates how to use TypeScript functions and types imported from bridge.d.ts
+@JS public func run() {
+ // Call the imported consoleLog function defined in bridge.d.ts
+ consoleLog("Hello, World!")
+
+ // Get the document object - this comes from the imported getDocument() function
+ let document = getDocument()
+
+ // Access and modify properties - the title property is read/write
+ document.title = "Hello, World!"
+
+ // Access read-only properties - body is defined as readonly in TypeScript
+ let body = document.body
+
+ // Create a new element using the document.createElement method
+ let h1 = document.createElement("h1")
+
+ // Set properties on the created element
+ h1.innerText = "Hello, World!"
+
+ // Call methods on objects - appendChild is defined in the HTMLElement interface
+ body.appendChild(h1)
+}
diff --git a/Examples/ImportTS/index.html b/Examples/ImportTS/index.html
new file mode 100644
index 00000000..31881c49
--- /dev/null
+++ b/Examples/ImportTS/index.html
@@ -0,0 +1,16 @@
+
+
+
+
+ Getting Started
+
+
+
+
+
+
+
+
+
+
+
diff --git a/Examples/ImportTS/index.js b/Examples/ImportTS/index.js
new file mode 100644
index 00000000..9452b7ec
--- /dev/null
+++ b/Examples/ImportTS/index.js
@@ -0,0 +1,13 @@
+import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
+const { exports } = await init({
+ imports: {
+ consoleLog: (message) => {
+ console.log(message);
+ },
+ getDocument: () => {
+ return document;
+ },
+ }
+});
+
+exports.run()
diff --git a/Examples/Multithreading/Package.resolved b/Examples/Multithreading/Package.resolved
index 1354cc03..f55b8400 100644
--- a/Examples/Multithreading/Package.resolved
+++ b/Examples/Multithreading/Package.resolved
@@ -1,5 +1,5 @@
{
- "originHash" : "e66f4c272838a860049b7e3528f1db03ee6ae99c2b21c3b6ea58a293be4db41b",
+ "originHash" : "072d03a6e24e01bd372682a6090adb80cf29dea39421e065de6ff8853de704c9",
"pins" : [
{
"identity" : "chibi-ray",
@@ -8,6 +8,15 @@
"state" : {
"revision" : "c8cab621a3338dd2f8e817d3785362409d3b8cf1"
}
+ },
+ {
+ "identity" : "swift-syntax",
+ "kind" : "remoteSourceControl",
+ "location" : "https://github.com/swiftlang/swift-syntax",
+ "state" : {
+ "revision" : "0687f71944021d616d34d922343dcef086855920",
+ "version" : "600.0.1"
+ }
}
],
"version" : 3
diff --git a/Makefile b/Makefile
index 93d7400e..761010bd 100644
--- a/Makefile
+++ b/Makefile
@@ -16,7 +16,7 @@ build:
.PHONY: unittest
unittest:
@echo Running unit tests
- swift package --swift-sdk "$(SWIFT_SDK_ID)" \
+ env JAVASCRIPTKIT_EXPERIMENTAL_BRIDGEJS=1 swift package --swift-sdk "$(SWIFT_SDK_ID)" \
--disable-sandbox \
-Xlinker --stack-first \
-Xlinker --global-base=524288 \
diff --git a/Package.swift b/Package.swift
index fcf40524..3657bfa9 100644
--- a/Package.swift
+++ b/Package.swift
@@ -1,5 +1,6 @@
// swift-tools-version:6.0
+import CompilerPluginSupport
import PackageDescription
// NOTE: needed for embedded customizations, ideally this will not be necessary at all in the future, or can be replaced with traits
@@ -9,12 +10,24 @@ let useLegacyResourceBundling =
let package = Package(
name: "JavaScriptKit",
+ platforms: [
+ .macOS(.v10_15),
+ .iOS(.v13),
+ .tvOS(.v13),
+ .watchOS(.v6),
+ .macCatalyst(.v13),
+ ],
products: [
.library(name: "JavaScriptKit", targets: ["JavaScriptKit"]),
.library(name: "JavaScriptEventLoop", targets: ["JavaScriptEventLoop"]),
.library(name: "JavaScriptBigIntSupport", targets: ["JavaScriptBigIntSupport"]),
.library(name: "JavaScriptEventLoopTestSupport", targets: ["JavaScriptEventLoopTestSupport"]),
.plugin(name: "PackageToJS", targets: ["PackageToJS"]),
+ .plugin(name: "BridgeJS", targets: ["BridgeJS"]),
+ .plugin(name: "BridgeJSCommandPlugin", targets: ["BridgeJSCommandPlugin"]),
+ ],
+ dependencies: [
+ .package(url: "https://github.com/swiftlang/swift-syntax", "600.0.0"..<"601.0.0")
],
targets: [
.target(
@@ -98,7 +111,40 @@ let package = Package(
capability: .command(
intent: .custom(verb: "js", description: "Convert a Swift package to a JavaScript package")
),
- sources: ["Sources"]
+ path: "Plugins/PackageToJS/Sources"
+ ),
+ .plugin(
+ name: "BridgeJS",
+ capability: .buildTool(),
+ dependencies: ["BridgeJSTool"],
+ path: "Plugins/BridgeJS/Sources/BridgeJSBuildPlugin"
+ ),
+ .plugin(
+ name: "BridgeJSCommandPlugin",
+ capability: .command(
+ intent: .custom(verb: "bridge-js", description: "Generate bridging code"),
+ permissions: [.writeToPackageDirectory(reason: "Generate bridging code")]
+ ),
+ dependencies: ["BridgeJSTool"],
+ path: "Plugins/BridgeJS/Sources/BridgeJSCommandPlugin"
+ ),
+ .executableTarget(
+ name: "BridgeJSTool",
+ dependencies: [
+ .product(name: "SwiftParser", package: "swift-syntax"),
+ .product(name: "SwiftSyntax", package: "swift-syntax"),
+ .product(name: "SwiftBasicFormat", package: "swift-syntax"),
+ .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
+ ],
+ path: "Plugins/BridgeJS/Sources/BridgeJSTool"
+ ),
+ .testTarget(
+ name: "BridgeJSRuntimeTests",
+ dependencies: ["JavaScriptKit"],
+ exclude: ["Generated/JavaScript"],
+ swiftSettings: [
+ .enableExperimentalFeature("Extern")
+ ]
),
]
)
diff --git a/Plugins/BridgeJS/Package.swift b/Plugins/BridgeJS/Package.swift
new file mode 100644
index 00000000..ab8b475c
--- /dev/null
+++ b/Plugins/BridgeJS/Package.swift
@@ -0,0 +1,29 @@
+// swift-tools-version: 6.0
+
+import PackageDescription
+
+let package = Package(
+ name: "BridgeJS",
+ platforms: [.macOS(.v13)],
+ dependencies: [
+ .package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.1")
+ ],
+ targets: [
+ .target(name: "BridgeJSBuildPlugin"),
+ .target(name: "BridgeJSLink"),
+ .executableTarget(
+ name: "BridgeJSTool",
+ dependencies: [
+ .product(name: "SwiftParser", package: "swift-syntax"),
+ .product(name: "SwiftSyntax", package: "swift-syntax"),
+ .product(name: "SwiftBasicFormat", package: "swift-syntax"),
+ .product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
+ ]
+ ),
+ .testTarget(
+ name: "BridgeJSToolTests",
+ dependencies: ["BridgeJSTool", "BridgeJSLink"],
+ exclude: ["__Snapshots__", "Inputs"]
+ ),
+ ]
+)
diff --git a/Plugins/BridgeJS/README.md b/Plugins/BridgeJS/README.md
new file mode 100644
index 00000000..9cbd0401
--- /dev/null
+++ b/Plugins/BridgeJS/README.md
@@ -0,0 +1,137 @@
+# BridgeJS
+
+> [!IMPORTANT]
+> This feature is still experimental, and the API may change frequently. Use at your own risk with `JAVASCRIPTKIT_EXPERIMENTAL_BRIDGEJS=1` environment variable.
+
+> [!NOTE]
+> This documentation is intended for JavaScriptKit developers, not JavaScriptKit users.
+
+## Overview
+
+BridgeJS provides easy interoperability between Swift and JavaScript/TypeScript. It enables:
+
+1. **Importing TypeScript APIs into Swift**: Use TypeScript/JavaScript APIs directly from Swift code
+2. **Exporting Swift APIs to JavaScript**: Make your Swift APIs available to JavaScript code
+
+## Architecture Diagram
+
+```mermaid
+graph LR
+ E1 --> G3[ExportSwift.json]
+ subgraph ModuleA
+ A.swift --> E1[[bridge-js export]]
+ B.swift --> E1
+ E1 --> G1[ExportSwift.swift]
+ B1[bridge.d.ts]-->I1[[bridge-js import]]
+ I1 --> G2[ImportTS.swift]
+ end
+ I1 --> G4[ImportTS.json]
+
+ E2 --> G7[ExportSwift.json]
+ subgraph ModuleB
+ C.swift --> E2[[bridge-js export]]
+ D.swift --> E2
+ E2 --> G5[ExportSwift.swift]
+ B2[bridge.d.ts]-->I2[[bridge-js import]]
+ I2 --> G6[ImportTS.swift]
+ end
+ I2 --> G8[ImportTS.json]
+
+ G3 --> L1[[bridge-js link]]
+ G4 --> L1
+ G7 --> L1
+ G8 --> L1
+
+ L1 --> F1[bridge.js]
+ L1 --> F2[bridge.d.ts]
+ ModuleA -----> App[App.wasm]
+ ModuleB -----> App
+
+ App --> PKG[[PackageToJS]]
+ F1 --> PKG
+ F2 --> PKG
+```
+
+## Type Mapping
+
+### Primitive Type Conversions
+
+TBD
+
+| Swift Type | JS Type | Wasm Core Type |
+|:--------------|:-----------|:---------------|
+| `Int` | `number` | `i32` |
+| `UInt` | `number` | `i32` |
+| `Int8` | `number` | `i32` |
+| `UInt8` | `number` | `i32` |
+| `Int16` | `number` | `i32` |
+| `UInt16` | `number` | `i32` |
+| `Int32` | `number` | `i32` |
+| `UInt32` | `number` | `i32` |
+| `Int64` | `bigint` | `i64` |
+| `UInt64` | `bigint` | `i64` |
+| `Float` | `number` | `f32` |
+| `Double` | `number` | `f64` |
+| `Bool` | `boolean` | `i32` |
+| `Void` | `void` | - |
+| `String` | `string` | `i32` |
+
+## Type Modeling
+
+TypeScript uses [structural subtyping](https://www.typescriptlang.org/docs/handbook/type-compatibility.html), but Swift doesn't directly offer it. We can't map every TypeScript type to Swift, so we made several give-ups and heuristics.
+
+### `interface`
+
+We intentionally don't simulate TS's `interface` with Swift's `protocol` even though they are quite similar for the following reasons:
+
+* Adding a protocol conformance for each `interface` implementation adds binary size cost in debug build because it's not easy to DCE.
+* No straightforward way to represent the use of `interface` type on the return type position of TS function. Which concrete type it should it be?
+* For Embedded Swift, we should avoid use of existential type as much as possible.
+
+Instead of simulating the subtyping-rule with Swift's `protocol`, we represent each `interface` with Swift's struct.
+In this way, we lose implicit type coercion but it makes things simpler and clear.
+
+TBD: Consider providing type-conversion methods to simulate subtyping rule like `func asIface()`
+
+### Anonymous type literals
+
+Swift offers a few non-nominal types, tuple and function types, but they are not enough to provide access to the underlying storage lazily. So we gave up importing them in typed way.
+
+## ABI
+
+This section describes the ABI contract used between JavaScript and Swift.
+The ABI will not be stable, and not meant to be interposed by other tools.
+
+### Parameter Passing
+
+Parameter passing follows Wasm calling conventions, with custom handling for complex types like strings and objects.
+
+TBD
+
+### Return Values
+
+TBD
+
+## Future Work
+
+- [ ] Struct on parameter or return type
+- [ ] Throws functions
+- [ ] Async functions
+- [ ] Cast between TS interface
+- [ ] Closure support
+- [ ] Simplify constructor pattern
+ * https://github.com/ocsigen/ts2ocaml/blob/main/docs/js_of_ocaml.md#feature-immediate-constructor
+ ```typescript
+ interface Foo = {
+ someMethod(value: number): void;
+ }
+
+ interface FooConstructor {
+ new(name: string) : Foo;
+
+ anotherMethod(): number;
+ }
+
+ declare var Foo: FooConstructor;
+ ```
+- [ ] Use `externref` once it's widely available
diff --git a/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift b/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift
new file mode 100644
index 00000000..4ea725ed
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSBuildPlugin/BridgeJSBuildPlugin.swift
@@ -0,0 +1,71 @@
+#if canImport(PackagePlugin)
+import PackagePlugin
+import Foundation
+
+/// Build plugin for runtime code generation with BridgeJS.
+/// This plugin automatically generates bridge code between Swift and JavaScript
+/// during each build process.
+@main
+struct BridgeJSBuildPlugin: BuildToolPlugin {
+ func createBuildCommands(context: PluginContext, target: Target) throws -> [Command] {
+ guard let swiftSourceModuleTarget = target as? SwiftSourceModuleTarget else {
+ return []
+ }
+ return try [
+ createExportSwiftCommand(context: context, target: swiftSourceModuleTarget),
+ createImportTSCommand(context: context, target: swiftSourceModuleTarget),
+ ]
+ }
+
+ private func createExportSwiftCommand(context: PluginContext, target: SwiftSourceModuleTarget) throws -> Command {
+ let outputSwiftPath = context.pluginWorkDirectoryURL.appending(path: "ExportSwift.swift")
+ let outputSkeletonPath = context.pluginWorkDirectoryURL.appending(path: "ExportSwift.json")
+ let inputFiles = target.sourceFiles.filter { !$0.url.path.hasPrefix(context.pluginWorkDirectoryURL.path + "/") }
+ .map(\.url)
+ return .buildCommand(
+ displayName: "Export Swift API",
+ executable: try context.tool(named: "BridgeJSTool").url,
+ arguments: [
+ "export",
+ "--output-skeleton",
+ outputSkeletonPath.path,
+ "--output-swift",
+ outputSwiftPath.path,
+ "--always-write", "true",
+ ] + inputFiles.map(\.path),
+ inputFiles: inputFiles,
+ outputFiles: [
+ outputSwiftPath
+ ]
+ )
+ }
+
+ private func createImportTSCommand(context: PluginContext, target: SwiftSourceModuleTarget) throws -> Command {
+ let outputSwiftPath = context.pluginWorkDirectoryURL.appending(path: "ImportTS.swift")
+ let outputSkeletonPath = context.pluginWorkDirectoryURL.appending(path: "ImportTS.json")
+ let inputFiles = [
+ target.directoryURL.appending(path: "bridge.d.ts")
+ ]
+ return .buildCommand(
+ displayName: "Import TypeScript API",
+ executable: try context.tool(named: "BridgeJSTool").url,
+ arguments: [
+ "import",
+ "--output-skeleton",
+ outputSkeletonPath.path,
+ "--output-swift",
+ outputSwiftPath.path,
+ "--module-name",
+ target.name,
+ "--always-write", "true",
+ "--project",
+ context.package.directoryURL.appending(path: "tsconfig.json").path,
+ ] + inputFiles.map(\.path),
+ inputFiles: inputFiles,
+ outputFiles: [
+ outputSwiftPath
+ ]
+ )
+ }
+}
+#endif
diff --git a/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift b/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift
new file mode 100644
index 00000000..286b052d
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSCommandPlugin/BridgeJSCommandPlugin.swift
@@ -0,0 +1,182 @@
+#if canImport(PackagePlugin)
+import PackagePlugin
+@preconcurrency import Foundation
+
+/// Command plugin for ahead-of-time (AOT) code generation with BridgeJS.
+/// This plugin allows you to generate bridge code between Swift and JavaScript
+/// before the build process, improving build times for larger projects.
+/// See documentation: Ahead-of-Time-Code-Generation.md
+@main
+struct BridgeJSCommandPlugin: CommandPlugin {
+ static let JAVASCRIPTKIT_PACKAGE_NAME: String = "JavaScriptKit"
+
+ struct Options {
+ var targets: [String]
+
+ static func parse(extractor: inout ArgumentExtractor) -> Options {
+ let targets = extractor.extractOption(named: "target")
+ return Options(targets: targets)
+ }
+
+ static func help() -> String {
+ return """
+ OVERVIEW: Generate ahead-of-time (AOT) bridge code between Swift and JavaScript.
+
+ This command generates bridge code before the build process, which can significantly
+ improve build times for larger projects by avoiding runtime code generation.
+ Generated code will be placed in the target's 'Generated' directory.
+
+ OPTIONS:
+ --target Specify target(s) to generate bridge code for. If omitted,
+ generates for all targets with JavaScriptKit dependency.
+ """
+ }
+ }
+
+ func performCommand(context: PluginContext, arguments: [String]) throws {
+ // Check for help flags to display usage information
+ // This allows users to run `swift package plugin bridge-js --help` to understand the plugin's functionality
+ if arguments.contains(where: { ["-h", "--help"].contains($0) }) {
+ printStderr(Options.help())
+ return
+ }
+
+ var extractor = ArgumentExtractor(arguments)
+ let options = Options.parse(extractor: &extractor)
+ let remainingArguments = extractor.remainingArguments
+
+ if options.targets.isEmpty {
+ try runOnTargets(
+ context: context,
+ remainingArguments: remainingArguments,
+ where: { target in
+ target.hasDependency(named: Self.JAVASCRIPTKIT_PACKAGE_NAME)
+ }
+ )
+ } else {
+ try runOnTargets(
+ context: context,
+ remainingArguments: remainingArguments,
+ where: { options.targets.contains($0.name) }
+ )
+ }
+ }
+
+ private func runOnTargets(
+ context: PluginContext,
+ remainingArguments: [String],
+ where predicate: (SwiftSourceModuleTarget) -> Bool
+ ) throws {
+ for target in context.package.targets {
+ guard let target = target as? SwiftSourceModuleTarget else {
+ continue
+ }
+ guard predicate(target) else {
+ continue
+ }
+ try runSingleTarget(context: context, target: target, remainingArguments: remainingArguments)
+ }
+ }
+
+ private func runSingleTarget(
+ context: PluginContext,
+ target: SwiftSourceModuleTarget,
+ remainingArguments: [String]
+ ) throws {
+ Diagnostics.progress("Exporting Swift API for \(target.name)...")
+
+ let generatedDirectory = target.directoryURL.appending(path: "Generated")
+ let generatedJavaScriptDirectory = generatedDirectory.appending(path: "JavaScript")
+
+ try runBridgeJSTool(
+ context: context,
+ arguments: [
+ "export",
+ "--output-skeleton",
+ generatedJavaScriptDirectory.appending(path: "ExportSwift.json").path,
+ "--output-swift",
+ generatedDirectory.appending(path: "ExportSwift.swift").path,
+ ]
+ + target.sourceFiles.filter {
+ !$0.url.path.hasPrefix(generatedDirectory.path + "/")
+ }.map(\.url.path) + remainingArguments
+ )
+
+ try runBridgeJSTool(
+ context: context,
+ arguments: [
+ "import",
+ "--output-skeleton",
+ generatedJavaScriptDirectory.appending(path: "ImportTS.json").path,
+ "--output-swift",
+ generatedDirectory.appending(path: "ImportTS.swift").path,
+ "--module-name",
+ target.name,
+ "--project",
+ context.package.directoryURL.appending(path: "tsconfig.json").path,
+ target.directoryURL.appending(path: "bridge.d.ts").path,
+ ] + remainingArguments
+ )
+ }
+
+ private func runBridgeJSTool(context: PluginContext, arguments: [String]) throws {
+ let tool = try context.tool(named: "BridgeJSTool").url
+ printStderr("$ \(tool.path) \(arguments.joined(separator: " "))")
+ let process = Process()
+ process.executableURL = tool
+ process.arguments = arguments
+ try process.forwardTerminationSignals {
+ try process.run()
+ process.waitUntilExit()
+ }
+ if process.terminationStatus != 0 {
+ exit(process.terminationStatus)
+ }
+ }
+}
+
+private func printStderr(_ message: String) {
+ fputs(message + "\n", stderr)
+}
+
+extension SwiftSourceModuleTarget {
+ func hasDependency(named name: String) -> Bool {
+ return dependencies.contains(where: {
+ switch $0 {
+ case .product(let product):
+ return product.name == name
+ case .target(let target):
+ return target.name == name
+ @unknown default:
+ return false
+ }
+ })
+ }
+}
+
+extension Foundation.Process {
+ // Monitor termination/interrruption signals to forward them to child process
+ func setSignalForwarding(_ signalNo: Int32) -> DispatchSourceSignal {
+ let signalSource = DispatchSource.makeSignalSource(signal: signalNo)
+ signalSource.setEventHandler { [self] in
+ signalSource.cancel()
+ kill(processIdentifier, signalNo)
+ }
+ signalSource.resume()
+ return signalSource
+ }
+
+ func forwardTerminationSignals(_ body: () throws -> Void) rethrows {
+ let sources = [
+ setSignalForwarding(SIGINT),
+ setSignalForwarding(SIGTERM),
+ ]
+ defer {
+ for source in sources {
+ source.cancel()
+ }
+ }
+ try body()
+ }
+}
+#endif
diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift
new file mode 100644
index 00000000..e62a9a63
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSLink.swift
@@ -0,0 +1,561 @@
+import Foundation
+
+struct BridgeJSLink {
+ /// The exported skeletons
+ var exportedSkeletons: [ExportedSkeleton] = []
+ var importedSkeletons: [ImportedModuleSkeleton] = []
+
+ mutating func addExportedSkeletonFile(data: Data) throws {
+ let skeleton = try JSONDecoder().decode(ExportedSkeleton.self, from: data)
+ exportedSkeletons.append(skeleton)
+ }
+
+ mutating func addImportedSkeletonFile(data: Data) throws {
+ let skeletons = try JSONDecoder().decode(ImportedModuleSkeleton.self, from: data)
+ importedSkeletons.append(skeletons)
+ }
+
+ let swiftHeapObjectClassDts = """
+ /// 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;
+ }
+ """
+
+ let swiftHeapObjectClassJs = """
+ /// Represents a Swift heap object like a class instance or an actor instance.
+ class SwiftHeapObject {
+ constructor(pointer, deinit) {
+ this.pointer = pointer;
+ this.hasReleased = false;
+ this.deinit = deinit;
+ this.registry = new FinalizationRegistry((pointer) => {
+ deinit(pointer);
+ });
+ this.registry.register(this, this.pointer);
+ }
+
+ release() {
+ this.registry.unregister(this);
+ this.deinit(this.pointer);
+ }
+ }
+ """
+
+ func link() throws -> (outputJs: String, outputDts: String) {
+ var exportsLines: [String] = []
+ var importedLines: [String] = []
+ var classLines: [String] = []
+ var dtsExportLines: [String] = []
+ var dtsImportLines: [String] = []
+ var dtsClassLines: [String] = []
+
+ if exportedSkeletons.contains(where: { $0.classes.count > 0 }) {
+ classLines.append(
+ contentsOf: swiftHeapObjectClassJs.split(separator: "\n", omittingEmptySubsequences: false).map {
+ String($0)
+ }
+ )
+ dtsClassLines.append(
+ contentsOf: swiftHeapObjectClassDts.split(separator: "\n", omittingEmptySubsequences: false).map {
+ String($0)
+ }
+ )
+ }
+
+ for skeleton in exportedSkeletons {
+ for klass in skeleton.classes {
+ let (jsType, dtsType, dtsExportEntry) = renderExportedClass(klass)
+ classLines.append(contentsOf: jsType)
+ exportsLines.append("\(klass.name),")
+ dtsExportLines.append(contentsOf: dtsExportEntry)
+ dtsClassLines.append(contentsOf: dtsType)
+ }
+
+ for function in skeleton.functions {
+ var (js, dts) = renderExportedFunction(function: function)
+ js[0] = "\(function.name): " + js[0]
+ js[js.count - 1] += ","
+ exportsLines.append(contentsOf: js)
+ dtsExportLines.append(contentsOf: dts)
+ }
+ }
+
+ for skeletonSet in importedSkeletons {
+ importedLines.append("const \(skeletonSet.moduleName) = importObject[\"\(skeletonSet.moduleName)\"] = {};")
+ func assignToImportObject(name: String, function: [String]) {
+ var js = function
+ js[0] = "\(skeletonSet.moduleName)[\"\(name)\"] = " + js[0]
+ importedLines.append(contentsOf: js)
+ }
+ for fileSkeleton in skeletonSet.children {
+ for function in fileSkeleton.functions {
+ let (js, dts) = try renderImportedFunction(function: function)
+ assignToImportObject(name: function.abiName(context: nil), function: js)
+ dtsImportLines.append(contentsOf: dts)
+ }
+ for type in fileSkeleton.types {
+ for property in type.properties {
+ let getterAbiName = property.getterAbiName(context: type)
+ let (js, dts) = try renderImportedProperty(
+ property: property,
+ abiName: getterAbiName,
+ emitCall: { thunkBuilder in
+ thunkBuilder.callPropertyGetter(name: property.name, returnType: property.type)
+ return try thunkBuilder.lowerReturnValue(returnType: property.type)
+ }
+ )
+ assignToImportObject(name: getterAbiName, function: js)
+ dtsImportLines.append(contentsOf: dts)
+
+ if !property.isReadonly {
+ let setterAbiName = property.setterAbiName(context: type)
+ let (js, dts) = try renderImportedProperty(
+ property: property,
+ abiName: setterAbiName,
+ emitCall: { thunkBuilder in
+ thunkBuilder.liftParameter(
+ param: Parameter(label: nil, name: "newValue", type: property.type)
+ )
+ thunkBuilder.callPropertySetter(name: property.name, returnType: property.type)
+ return nil
+ }
+ )
+ assignToImportObject(name: setterAbiName, function: js)
+ dtsImportLines.append(contentsOf: dts)
+ }
+ }
+ for method in type.methods {
+ let (js, dts) = try renderImportedMethod(context: type, method: method)
+ assignToImportObject(name: method.abiName(context: type), function: js)
+ dtsImportLines.append(contentsOf: dts)
+ }
+ }
+ }
+ }
+
+ let outputJs = """
+ // 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ \(importedLines.map { $0.indent(count: 12) }.joined(separator: "\n"))
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+ \(classLines.map { $0.indent(count: 12) }.joined(separator: "\n"))
+ return {
+ \(exportsLines.map { $0.indent(count: 16) }.joined(separator: "\n"))
+ };
+ },
+ }
+ }
+ """
+ var dtsLines: [String] = []
+ dtsLines.append(contentsOf: dtsClassLines)
+ dtsLines.append("export type Exports = {")
+ dtsLines.append(contentsOf: dtsExportLines.map { $0.indent(count: 4) })
+ dtsLines.append("}")
+ dtsLines.append("export type Imports = {")
+ dtsLines.append(contentsOf: dtsImportLines.map { $0.indent(count: 4) })
+ dtsLines.append("}")
+ let outputDts = """
+ // 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`.
+
+ \(dtsLines.joined(separator: "\n"))
+ export function createInstantiator(options: {
+ imports: Imports;
+ }, swift: any): Promise<{
+ addImports: (importObject: WebAssembly.Imports) => void;
+ setInstance: (instance: WebAssembly.Instance) => void;
+ createExports: (instance: WebAssembly.Instance) => Exports;
+ }>;
+ """
+ return (outputJs, outputDts)
+ }
+
+ class ExportedThunkBuilder {
+ var bodyLines: [String] = []
+ var cleanupLines: [String] = []
+ var parameterForwardings: [String] = []
+
+ func lowerParameter(param: Parameter) {
+ switch param.type {
+ case .void: return
+ case .int, .float, .double, .bool:
+ parameterForwardings.append(param.name)
+ case .string:
+ let bytesLabel = "\(param.name)Bytes"
+ let bytesIdLabel = "\(param.name)Id"
+ bodyLines.append("const \(bytesLabel) = textEncoder.encode(\(param.name));")
+ bodyLines.append("const \(bytesIdLabel) = swift.memory.retain(\(bytesLabel));")
+ cleanupLines.append("swift.memory.release(\(bytesIdLabel));")
+ parameterForwardings.append(bytesIdLabel)
+ parameterForwardings.append("\(bytesLabel).length")
+ case .jsObject:
+ parameterForwardings.append("swift.memory.retain(\(param.name))")
+ case .swiftHeapObject:
+ parameterForwardings.append("\(param.name).pointer")
+ }
+ }
+
+ func lowerSelf() {
+ parameterForwardings.append("this.pointer")
+ }
+
+ func call(abiName: String, returnType: BridgeType) -> String? {
+ let call = "instance.exports.\(abiName)(\(parameterForwardings.joined(separator: ", ")))"
+ var returnExpr: String?
+
+ switch returnType {
+ case .void:
+ bodyLines.append("\(call);")
+ case .string:
+ bodyLines.append("\(call);")
+ bodyLines.append("const ret = tmpRetString;")
+ bodyLines.append("tmpRetString = undefined;")
+ returnExpr = "ret"
+ case .int, .float, .double:
+ bodyLines.append("const ret = \(call);")
+ returnExpr = "ret"
+ case .bool:
+ bodyLines.append("const ret = \(call) !== 0;")
+ returnExpr = "ret"
+ case .jsObject:
+ bodyLines.append("const retId = \(call);")
+ // TODO: Implement "take" operation
+ bodyLines.append("const ret = swift.memory.getObject(retId);")
+ bodyLines.append("swift.memory.release(retId);")
+ returnExpr = "ret"
+ case .swiftHeapObject(let name):
+ bodyLines.append("const ret = new \(name)(\(call));")
+ returnExpr = "ret"
+ }
+ return returnExpr
+ }
+
+ func callConstructor(abiName: String) -> String {
+ return "instance.exports.\(abiName)(\(parameterForwardings.joined(separator: ", ")))"
+ }
+
+ func renderFunction(
+ name: String,
+ parameters: [Parameter],
+ returnType: BridgeType,
+ returnExpr: String?,
+ isMethod: Bool
+ ) -> [String] {
+ var funcLines: [String] = []
+ funcLines.append(
+ "\(isMethod ? "" : "function ")\(name)(\(parameters.map { $0.name }.joined(separator: ", "))) {"
+ )
+ funcLines.append(contentsOf: bodyLines.map { $0.indent(count: 4) })
+ funcLines.append(contentsOf: cleanupLines.map { $0.indent(count: 4) })
+ if let returnExpr = returnExpr {
+ funcLines.append("return \(returnExpr);".indent(count: 4))
+ }
+ funcLines.append("}")
+ return funcLines
+ }
+ }
+
+ private func renderTSSignature(parameters: [Parameter], returnType: BridgeType) -> String {
+ return "(\(parameters.map { "\($0.name): \($0.type.tsType)" }.joined(separator: ", "))): \(returnType.tsType)"
+ }
+
+ func renderExportedFunction(function: ExportedFunction) -> (js: [String], dts: [String]) {
+ let thunkBuilder = ExportedThunkBuilder()
+ for param in function.parameters {
+ thunkBuilder.lowerParameter(param: param)
+ }
+ let returnExpr = thunkBuilder.call(abiName: function.abiName, returnType: function.returnType)
+ let funcLines = thunkBuilder.renderFunction(
+ name: function.abiName,
+ parameters: function.parameters,
+ returnType: function.returnType,
+ returnExpr: returnExpr,
+ isMethod: false
+ )
+ var dtsLines: [String] = []
+ dtsLines.append(
+ "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));"
+ )
+
+ return (funcLines, dtsLines)
+ }
+
+ func renderExportedClass(_ klass: ExportedClass) -> (js: [String], dtsType: [String], dtsExportEntry: [String]) {
+ var jsLines: [String] = []
+ var dtsTypeLines: [String] = []
+ var dtsExportEntryLines: [String] = []
+
+ dtsTypeLines.append("export interface \(klass.name) extends SwiftHeapObject {")
+ dtsExportEntryLines.append("\(klass.name): {")
+ jsLines.append("class \(klass.name) extends SwiftHeapObject {")
+
+ if let constructor: ExportedConstructor = klass.constructor {
+ let thunkBuilder = ExportedThunkBuilder()
+ for param in constructor.parameters {
+ thunkBuilder.lowerParameter(param: param)
+ }
+ let returnExpr = thunkBuilder.callConstructor(abiName: constructor.abiName)
+ var funcLines: [String] = []
+ funcLines.append("constructor(\(constructor.parameters.map { $0.name }.joined(separator: ", "))) {")
+ funcLines.append(contentsOf: thunkBuilder.bodyLines.map { $0.indent(count: 4) })
+ funcLines.append("super(\(returnExpr), instance.exports.bjs_\(klass.name)_deinit);".indent(count: 4))
+ funcLines.append(contentsOf: thunkBuilder.cleanupLines.map { $0.indent(count: 4) })
+ funcLines.append("}")
+ jsLines.append(contentsOf: funcLines.map { $0.indent(count: 4) })
+
+ dtsExportEntryLines.append(
+ "new\(renderTSSignature(parameters: constructor.parameters, returnType: .swiftHeapObject(klass.name)));"
+ .indent(count: 4)
+ )
+ }
+
+ for method in klass.methods {
+ let thunkBuilder = ExportedThunkBuilder()
+ thunkBuilder.lowerSelf()
+ for param in method.parameters {
+ thunkBuilder.lowerParameter(param: param)
+ }
+ let returnExpr = thunkBuilder.call(abiName: method.abiName, returnType: method.returnType)
+ jsLines.append(
+ contentsOf: thunkBuilder.renderFunction(
+ name: method.name,
+ parameters: method.parameters,
+ returnType: method.returnType,
+ returnExpr: returnExpr,
+ isMethod: true
+ ).map { $0.indent(count: 4) }
+ )
+ dtsTypeLines.append(
+ "\(method.name)\(renderTSSignature(parameters: method.parameters, returnType: method.returnType));"
+ .indent(count: 4)
+ )
+ }
+ jsLines.append("}")
+
+ dtsTypeLines.append("}")
+ dtsExportEntryLines.append("}")
+
+ return (jsLines, dtsTypeLines, dtsExportEntryLines)
+ }
+
+ class ImportedThunkBuilder {
+ var bodyLines: [String] = []
+ var parameterNames: [String] = []
+ var parameterForwardings: [String] = []
+
+ func liftSelf() {
+ parameterNames.append("self")
+ }
+
+ func liftParameter(param: Parameter) {
+ parameterNames.append(param.name)
+ switch param.type {
+ case .string:
+ let stringObjectName = "\(param.name)Object"
+ // TODO: Implement "take" operation
+ bodyLines.append("const \(stringObjectName) = swift.memory.getObject(\(param.name));")
+ bodyLines.append("swift.memory.release(\(param.name));")
+ parameterForwardings.append(stringObjectName)
+ case .jsObject:
+ parameterForwardings.append("swift.memory.getObject(\(param.name))")
+ default:
+ parameterForwardings.append(param.name)
+ }
+ }
+
+ func renderFunction(
+ name: String,
+ returnExpr: String?
+ ) -> [String] {
+ var funcLines: [String] = []
+ funcLines.append(
+ "function \(name)(\(parameterNames.joined(separator: ", "))) {"
+ )
+ funcLines.append(contentsOf: bodyLines.map { $0.indent(count: 4) })
+ if let returnExpr = returnExpr {
+ funcLines.append("return \(returnExpr);".indent(count: 4))
+ }
+ funcLines.append("}")
+ return funcLines
+ }
+
+ func call(name: String, returnType: BridgeType) {
+ let call = "options.imports.\(name)(\(parameterForwardings.joined(separator: ", ")))"
+ if returnType == .void {
+ bodyLines.append("\(call);")
+ } else {
+ bodyLines.append("let ret = \(call);")
+ }
+ }
+
+ func callMethod(name: String, returnType: BridgeType) {
+ let call = "swift.memory.getObject(self).\(name)(\(parameterForwardings.joined(separator: ", ")))"
+ if returnType == .void {
+ bodyLines.append("\(call);")
+ } else {
+ bodyLines.append("let ret = \(call);")
+ }
+ }
+
+ func callPropertyGetter(name: String, returnType: BridgeType) {
+ let call = "swift.memory.getObject(self).\(name)"
+ bodyLines.append("let ret = \(call);")
+ }
+
+ func callPropertySetter(name: String, returnType: BridgeType) {
+ let call = "swift.memory.getObject(self).\(name) = \(parameterForwardings.joined(separator: ", "))"
+ bodyLines.append("\(call);")
+ }
+
+ func lowerReturnValue(returnType: BridgeType) throws -> String? {
+ switch returnType {
+ case .void:
+ return nil
+ case .string:
+ bodyLines.append("tmpRetBytes = textEncoder.encode(ret);")
+ return "tmpRetBytes.length"
+ case .int, .float, .double:
+ return "ret"
+ case .bool:
+ return "ret !== 0"
+ case .jsObject:
+ return "swift.memory.retain(ret)"
+ case .swiftHeapObject:
+ throw BridgeJSLinkError(message: "Swift heap object is not supported in imported functions")
+ }
+ }
+ }
+
+ func renderImportedFunction(function: ImportedFunctionSkeleton) throws -> (js: [String], dts: [String]) {
+ let thunkBuilder = ImportedThunkBuilder()
+ for param in function.parameters {
+ thunkBuilder.liftParameter(param: param)
+ }
+ thunkBuilder.call(name: function.name, returnType: function.returnType)
+ let returnExpr = try thunkBuilder.lowerReturnValue(returnType: function.returnType)
+ let funcLines = thunkBuilder.renderFunction(
+ name: function.abiName(context: nil),
+ returnExpr: returnExpr
+ )
+ var dtsLines: [String] = []
+ dtsLines.append(
+ "\(function.name)\(renderTSSignature(parameters: function.parameters, returnType: function.returnType));"
+ )
+ return (funcLines, dtsLines)
+ }
+
+ func renderImportedProperty(
+ property: ImportedPropertySkeleton,
+ abiName: String,
+ emitCall: (ImportedThunkBuilder) throws -> String?
+ ) throws -> (js: [String], dts: [String]) {
+ let thunkBuilder = ImportedThunkBuilder()
+ thunkBuilder.liftSelf()
+ let returnExpr = try emitCall(thunkBuilder)
+ let funcLines = thunkBuilder.renderFunction(
+ name: abiName,
+ returnExpr: returnExpr
+ )
+ return (funcLines, [])
+ }
+
+ func renderImportedMethod(
+ context: ImportedTypeSkeleton,
+ method: ImportedFunctionSkeleton
+ ) throws -> (js: [String], dts: [String]) {
+ let thunkBuilder = ImportedThunkBuilder()
+ thunkBuilder.liftSelf()
+ for param in method.parameters {
+ thunkBuilder.liftParameter(param: param)
+ }
+ thunkBuilder.callMethod(name: method.name, returnType: method.returnType)
+ let returnExpr = try thunkBuilder.lowerReturnValue(returnType: method.returnType)
+ let funcLines = thunkBuilder.renderFunction(
+ name: method.abiName(context: context),
+ returnExpr: returnExpr
+ )
+ return (funcLines, [])
+ }
+}
+
+struct BridgeJSLinkError: Error {
+ let message: String
+}
+
+extension String {
+ func indent(count: Int) -> String {
+ return String(repeating: " ", count: count) + self
+ }
+}
+
+extension BridgeType {
+ var tsType: String {
+ switch self {
+ case .void:
+ return "void"
+ case .string:
+ return "string"
+ case .int:
+ return "number"
+ case .float:
+ return "number"
+ case .double:
+ return "number"
+ case .bool:
+ return "boolean"
+ case .jsObject:
+ return "any"
+ case .swiftHeapObject(let name):
+ return name
+ }
+ }
+}
diff --git a/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSSkeleton b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSSkeleton
new file mode 120000
index 00000000..a2c26678
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSLink/BridgeJSSkeleton
@@ -0,0 +1 @@
+../BridgeJSSkeleton
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift
new file mode 100644
index 00000000..34492682
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSSkeleton/BridgeJSSkeleton.swift
@@ -0,0 +1,96 @@
+// This file is shared between BridgeTool and BridgeJSLink
+
+// MARK: - Types
+
+enum BridgeType: Codable, Equatable {
+ case int, float, double, string, bool, jsObject(String?), swiftHeapObject(String), void
+}
+
+enum WasmCoreType: String, Codable {
+ case i32, i64, f32, f64, pointer
+}
+
+struct Parameter: Codable {
+ let label: String?
+ let name: String
+ let type: BridgeType
+}
+
+// MARK: - Exported Skeleton
+
+struct ExportedFunction: Codable {
+ var name: String
+ var abiName: String
+ var parameters: [Parameter]
+ var returnType: BridgeType
+}
+
+struct ExportedClass: Codable {
+ var name: String
+ var constructor: ExportedConstructor?
+ var methods: [ExportedFunction]
+}
+
+struct ExportedConstructor: Codable {
+ var abiName: String
+ var parameters: [Parameter]
+}
+
+struct ExportedSkeleton: Codable {
+ let functions: [ExportedFunction]
+ let classes: [ExportedClass]
+}
+
+// MARK: - Imported Skeleton
+
+struct ImportedFunctionSkeleton: Codable {
+ let name: String
+ let parameters: [Parameter]
+ let returnType: BridgeType
+ let documentation: String?
+
+ func abiName(context: ImportedTypeSkeleton?) -> String {
+ return context.map { "bjs_\($0.name)_\(name)" } ?? "bjs_\(name)"
+ }
+}
+
+struct ImportedConstructorSkeleton: Codable {
+ let parameters: [Parameter]
+
+ func abiName(context: ImportedTypeSkeleton) -> String {
+ return "bjs_\(context.name)_init"
+ }
+}
+
+struct ImportedPropertySkeleton: Codable {
+ let name: String
+ let isReadonly: Bool
+ let type: BridgeType
+ let documentation: String?
+
+ func getterAbiName(context: ImportedTypeSkeleton) -> String {
+ return "bjs_\(context.name)_\(name)_get"
+ }
+
+ func setterAbiName(context: ImportedTypeSkeleton) -> String {
+ return "bjs_\(context.name)_\(name)_set"
+ }
+}
+
+struct ImportedTypeSkeleton: Codable {
+ let name: String
+ let constructor: ImportedConstructorSkeleton?
+ let methods: [ImportedFunctionSkeleton]
+ let properties: [ImportedPropertySkeleton]
+ let documentation: String?
+}
+
+struct ImportedFileSkeleton: Codable {
+ let functions: [ImportedFunctionSkeleton]
+ let types: [ImportedTypeSkeleton]
+}
+
+struct ImportedModuleSkeleton: Codable {
+ let moduleName: String
+ var children: [ImportedFileSkeleton]
+}
diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSSkeleton b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSSkeleton
new file mode 120000
index 00000000..a2c26678
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSSkeleton
@@ -0,0 +1 @@
+../BridgeJSSkeleton
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift
new file mode 100644
index 00000000..a6bd5ff5
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSTool/BridgeJSTool.swift
@@ -0,0 +1,340 @@
+@preconcurrency import func Foundation.exit
+@preconcurrency import func Foundation.fputs
+@preconcurrency import var Foundation.stderr
+@preconcurrency import struct Foundation.URL
+@preconcurrency import struct Foundation.Data
+@preconcurrency import class Foundation.JSONEncoder
+@preconcurrency import class Foundation.FileManager
+@preconcurrency import class Foundation.JSONDecoder
+@preconcurrency import class Foundation.ProcessInfo
+import SwiftParser
+
+/// BridgeJS Tool
+///
+/// A command-line tool to generate Swift-JavaScript bridge code for WebAssembly applications.
+/// This tool enables bidirectional interoperability between Swift and JavaScript:
+///
+/// 1. Import: Generate Swift bindings for TypeScript declarations
+/// 2. Export: Generate JavaScript bindings for Swift declarations
+///
+/// Usage:
+/// For importing TypeScript:
+/// $ bridge-js import --module-name --output-swift --output-skeleton --project
+/// For exporting Swift:
+/// $ bridge-js export --output-swift --output-skeleton
+///
+/// This tool is intended to be used through the Swift Package Manager plugin system
+/// and is not typically called directly by end users.
+@main struct BridgeJSTool {
+
+ static func help() -> String {
+ return """
+ Usage: \(CommandLine.arguments.first ?? "bridge-js-tool") [options]
+
+ Subcommands:
+ import Generate binding code to import TypeScript APIs into Swift
+ export Generate binding code to export Swift APIs to JavaScript
+ """
+ }
+
+ static func main() throws {
+ do {
+ try run()
+ } catch {
+ printStderr("Error: \(error)")
+ exit(1)
+ }
+ }
+
+ static func run() throws {
+ let arguments = Array(CommandLine.arguments.dropFirst())
+ guard let subcommand = arguments.first else {
+ throw BridgeJSToolError(
+ """
+ Error: No subcommand provided
+
+ \(BridgeJSTool.help())
+ """
+ )
+ }
+ let progress = ProgressReporting()
+ switch subcommand {
+ case "import":
+ let parser = ArgumentParser(
+ singleDashOptions: [:],
+ doubleDashOptions: [
+ "module-name": OptionRule(
+ help: "The name of the module to import the TypeScript API into",
+ required: true
+ ),
+ "always-write": OptionRule(
+ help: "Always write the output files even if no APIs are imported",
+ required: false
+ ),
+ "output-swift": OptionRule(help: "The output file path for the Swift source code", required: true),
+ "output-skeleton": OptionRule(
+ help: "The output file path for the skeleton of the imported TypeScript APIs",
+ required: true
+ ),
+ "project": OptionRule(
+ help: "The path to the TypeScript project configuration file",
+ required: true
+ ),
+ ]
+ )
+ let (positionalArguments, _, doubleDashOptions) = try parser.parse(
+ arguments: Array(arguments.dropFirst())
+ )
+ var importer = ImportTS(progress: progress, moduleName: doubleDashOptions["module-name"]!)
+ for inputFile in positionalArguments {
+ if inputFile.hasSuffix(".json") {
+ let sourceURL = URL(fileURLWithPath: inputFile)
+ let skeleton = try JSONDecoder().decode(
+ ImportedFileSkeleton.self,
+ from: Data(contentsOf: sourceURL)
+ )
+ importer.addSkeleton(skeleton)
+ } else if inputFile.hasSuffix(".d.ts") {
+ let tsconfigPath = URL(fileURLWithPath: doubleDashOptions["project"]!)
+ try importer.addSourceFile(inputFile, tsconfigPath: tsconfigPath.path)
+ }
+ }
+
+ let outputSwift = try importer.finalize()
+ let shouldWrite = doubleDashOptions["always-write"] == "true" || outputSwift != nil
+ guard shouldWrite else {
+ progress.print("No imported TypeScript APIs found")
+ return
+ }
+
+ let outputSwiftURL = URL(fileURLWithPath: doubleDashOptions["output-swift"]!)
+ try FileManager.default.createDirectory(
+ at: outputSwiftURL.deletingLastPathComponent(),
+ withIntermediateDirectories: true,
+ attributes: nil
+ )
+ try (outputSwift ?? "").write(to: outputSwiftURL, atomically: true, encoding: .utf8)
+
+ let outputSkeletonsURL = URL(fileURLWithPath: doubleDashOptions["output-skeleton"]!)
+ try FileManager.default.createDirectory(
+ at: outputSkeletonsURL.deletingLastPathComponent(),
+ withIntermediateDirectories: true,
+ attributes: nil
+ )
+ let encoder = JSONEncoder()
+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
+ try encoder.encode(importer.skeleton).write(to: outputSkeletonsURL)
+
+ progress.print(
+ """
+ Imported TypeScript APIs:
+ - \(outputSwiftURL.path)
+ - \(outputSkeletonsURL.path)
+ """
+ )
+ case "export":
+ let parser = ArgumentParser(
+ singleDashOptions: [:],
+ doubleDashOptions: [
+ "output-skeleton": OptionRule(
+ help: "The output file path for the skeleton of the exported Swift APIs",
+ required: true
+ ),
+ "output-swift": OptionRule(help: "The output file path for the Swift source code", required: true),
+ "always-write": OptionRule(
+ help: "Always write the output files even if no APIs are exported",
+ required: false
+ ),
+ ]
+ )
+ let (positionalArguments, _, doubleDashOptions) = try parser.parse(
+ arguments: Array(arguments.dropFirst())
+ )
+ let exporter = ExportSwift(progress: progress)
+ for inputFile in positionalArguments {
+ let sourceURL = URL(fileURLWithPath: inputFile)
+ guard sourceURL.pathExtension == "swift" else { continue }
+ let sourceContent = try String(contentsOf: sourceURL, encoding: .utf8)
+ let sourceFile = Parser.parse(source: sourceContent)
+ try exporter.addSourceFile(sourceFile, sourceURL.path)
+ }
+
+ // Finalize the export
+ let output = try exporter.finalize()
+ let outputSwiftURL = URL(fileURLWithPath: doubleDashOptions["output-swift"]!)
+ let outputSkeletonURL = URL(fileURLWithPath: doubleDashOptions["output-skeleton"]!)
+
+ let shouldWrite = doubleDashOptions["always-write"] == "true" || output != nil
+ guard shouldWrite else {
+ progress.print("No exported Swift APIs found")
+ return
+ }
+
+ // Create the output directory if it doesn't exist
+ try FileManager.default.createDirectory(
+ at: outputSwiftURL.deletingLastPathComponent(),
+ withIntermediateDirectories: true,
+ attributes: nil
+ )
+ try FileManager.default.createDirectory(
+ at: outputSkeletonURL.deletingLastPathComponent(),
+ withIntermediateDirectories: true,
+ attributes: nil
+ )
+
+ // Write the output Swift file
+ try (output?.outputSwift ?? "").write(to: outputSwiftURL, atomically: true, encoding: .utf8)
+
+ if let outputSkeleton = output?.outputSkeleton {
+ // Write the output skeleton file
+ let encoder = JSONEncoder()
+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
+ let outputSkeletonData = try encoder.encode(outputSkeleton)
+ try outputSkeletonData.write(to: outputSkeletonURL)
+ }
+ progress.print(
+ """
+ Exported Swift APIs:
+ - \(outputSwiftURL.path)
+ - \(outputSkeletonURL.path)
+ """
+ )
+ default:
+ throw BridgeJSToolError(
+ """
+ Error: Invalid subcommand: \(subcommand)
+
+ \(BridgeJSTool.help())
+ """
+ )
+ }
+ }
+}
+
+internal func which(_ executable: String) throws -> URL {
+ do {
+ // Check overriding environment variable
+ let envVariable = executable.uppercased().replacingOccurrences(of: "-", with: "_") + "_PATH"
+ if let path = ProcessInfo.processInfo.environment[envVariable] {
+ let url = URL(fileURLWithPath: path).appendingPathComponent(executable)
+ if FileManager.default.isExecutableFile(atPath: url.path) {
+ return url
+ }
+ }
+ }
+ let pathSeparator: Character
+ #if os(Windows)
+ pathSeparator = ";"
+ #else
+ pathSeparator = ":"
+ #endif
+ let paths = ProcessInfo.processInfo.environment["PATH"]!.split(separator: pathSeparator)
+ for path in paths {
+ let url = URL(fileURLWithPath: String(path)).appendingPathComponent(executable)
+ if FileManager.default.isExecutableFile(atPath: url.path) {
+ return url
+ }
+ }
+ throw BridgeJSToolError("Executable \(executable) not found in PATH")
+}
+
+struct BridgeJSToolError: Swift.Error, CustomStringConvertible {
+ let description: String
+
+ init(_ message: String) {
+ self.description = message
+ }
+}
+
+private func printStderr(_ message: String) {
+ fputs(message + "\n", stderr)
+}
+
+struct ProgressReporting {
+ let print: (String) -> Void
+
+ init(print: @escaping (String) -> Void = { Swift.print($0) }) {
+ self.print = print
+ }
+
+ static var silent: ProgressReporting {
+ return ProgressReporting(print: { _ in })
+ }
+
+ func print(_ message: String) {
+ self.print(message)
+ }
+}
+
+// MARK: - Minimal Argument Parsing
+
+struct OptionRule {
+ var help: String
+ var required: Bool = false
+}
+
+struct ArgumentParser {
+
+ let singleDashOptions: [String: OptionRule]
+ let doubleDashOptions: [String: OptionRule]
+
+ init(singleDashOptions: [String: OptionRule], doubleDashOptions: [String: OptionRule]) {
+ self.singleDashOptions = singleDashOptions
+ self.doubleDashOptions = doubleDashOptions
+ }
+
+ typealias ParsedArguments = (
+ positionalArguments: [String],
+ singleDashOptions: [String: String],
+ doubleDashOptions: [String: String]
+ )
+
+ func help() -> String {
+ var help = "Usage: \(CommandLine.arguments.first ?? "bridge-js-tool") [options] \n\n"
+ help += "Options:\n"
+ // Align the options by the longest option
+ let maxOptionLength = max(
+ (singleDashOptions.keys.map(\.count).max() ?? 0) + 1,
+ (doubleDashOptions.keys.map(\.count).max() ?? 0) + 2
+ )
+ for (key, rule) in singleDashOptions {
+ help += " -\(key)\(String(repeating: " ", count: maxOptionLength - key.count)): \(rule.help)\n"
+ }
+ for (key, rule) in doubleDashOptions {
+ help += " --\(key)\(String(repeating: " ", count: maxOptionLength - key.count)): \(rule.help)\n"
+ }
+ return help
+ }
+
+ func parse(arguments: [String]) throws -> ParsedArguments {
+ var positionalArguments: [String] = []
+ var singleDashOptions: [String: String] = [:]
+ var doubleDashOptions: [String: String] = [:]
+
+ var arguments = arguments.makeIterator()
+
+ while let arg = arguments.next() {
+ if arg.starts(with: "-") {
+ if arg.starts(with: "--") {
+ let key = String(arg.dropFirst(2))
+ let value = arguments.next()
+ doubleDashOptions[key] = value
+ } else {
+ let key = String(arg.dropFirst(1))
+ let value = arguments.next()
+ singleDashOptions[key] = value
+ }
+ } else {
+ positionalArguments.append(arg)
+ }
+ }
+
+ for (key, rule) in self.doubleDashOptions {
+ if rule.required, doubleDashOptions[key] == nil {
+ throw BridgeJSToolError("Option --\(key) is required")
+ }
+ }
+
+ return (positionalArguments, singleDashOptions, doubleDashOptions)
+ }
+}
diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/DiagnosticError.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/DiagnosticError.swift
new file mode 100644
index 00000000..2688f8da
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSTool/DiagnosticError.swift
@@ -0,0 +1,23 @@
+import SwiftSyntax
+
+struct DiagnosticError: Error {
+ let node: Syntax
+ let message: String
+ let hint: String?
+
+ init(node: some SyntaxProtocol, message: String, hint: String? = nil) {
+ self.node = Syntax(node)
+ self.message = message
+ self.hint = hint
+ }
+
+ func formattedDescription(fileName: String) -> String {
+ let locationConverter = SourceLocationConverter(fileName: fileName, tree: node.root)
+ let location = locationConverter.location(for: node.position)
+ var description = "\(fileName):\(location.line):\(location.column): error: \(message)"
+ if let hint {
+ description += "\nHint: \(hint)"
+ }
+ return description
+ }
+}
diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/ExportSwift.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/ExportSwift.swift
new file mode 100644
index 00000000..bef43bbc
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSTool/ExportSwift.swift
@@ -0,0 +1,599 @@
+import SwiftBasicFormat
+import SwiftSyntax
+import SwiftSyntaxBuilder
+import class Foundation.FileManager
+
+/// Exports Swift functions and classes to JavaScript
+///
+/// This class processes Swift source files to find declarations marked with `@JS`
+/// and generates:
+/// 1. Swift glue code to call the Swift functions from JavaScript
+/// 2. Skeleton files that define the structure of the exported APIs
+///
+/// The generated skeletons will be used by ``BridgeJSLink`` to generate
+/// JavaScript glue code and TypeScript definitions.
+class ExportSwift {
+ let progress: ProgressReporting
+
+ private var exportedFunctions: [ExportedFunction] = []
+ private var exportedClasses: [ExportedClass] = []
+ private var typeDeclResolver: TypeDeclResolver = TypeDeclResolver()
+
+ init(progress: ProgressReporting = ProgressReporting()) {
+ self.progress = progress
+ }
+
+ /// Processes a Swift source file to find declarations marked with @JS
+ ///
+ /// - Parameters:
+ /// - sourceFile: The parsed Swift source file to process
+ /// - inputFilePath: The file path for error reporting
+ func addSourceFile(_ sourceFile: SourceFileSyntax, _ inputFilePath: String) throws {
+ progress.print("Processing \(inputFilePath)")
+ typeDeclResolver.addSourceFile(sourceFile)
+
+ let errors = try parseSingleFile(sourceFile)
+ if errors.count > 0 {
+ throw BridgeJSToolError(
+ errors.map { $0.formattedDescription(fileName: inputFilePath) }
+ .joined(separator: "\n")
+ )
+ }
+ }
+
+ /// Finalizes the export process and generates the bridge code
+ ///
+ /// - Returns: A tuple containing the generated Swift code and a skeleton
+ /// describing the exported APIs
+ func finalize() throws -> (outputSwift: String, outputSkeleton: ExportedSkeleton)? {
+ guard let outputSwift = renderSwiftGlue() else {
+ return nil
+ }
+ return (
+ outputSwift: outputSwift,
+ outputSkeleton: ExportedSkeleton(functions: exportedFunctions, classes: exportedClasses)
+ )
+ }
+
+ fileprivate final class APICollector: SyntaxAnyVisitor {
+ var exportedFunctions: [ExportedFunction] = []
+ var exportedClasses: [String: ExportedClass] = [:]
+ var errors: [DiagnosticError] = []
+
+ enum State {
+ case topLevel
+ case classBody(name: String)
+ }
+
+ struct StateStack {
+ private var states: [State]
+ var current: State {
+ return states.last!
+ }
+
+ init(_ initialState: State) {
+ self.states = [initialState]
+ }
+ mutating func push(state: State) {
+ states.append(state)
+ }
+
+ mutating func pop() {
+ _ = states.removeLast()
+ }
+ }
+
+ var stateStack: StateStack = StateStack(.topLevel)
+ var state: State {
+ return stateStack.current
+ }
+ let parent: ExportSwift
+
+ init(parent: ExportSwift) {
+ self.parent = parent
+ super.init(viewMode: .sourceAccurate)
+ }
+
+ private func diagnose(node: some SyntaxProtocol, message: String, hint: String? = nil) {
+ errors.append(DiagnosticError(node: node, message: message, hint: hint))
+ }
+
+ private func diagnoseUnsupportedType(node: some SyntaxProtocol, type: String) {
+ diagnose(
+ node: node,
+ message: "Unsupported type: \(type)",
+ hint: "Only primitive types and types defined in the same module are allowed"
+ )
+ }
+
+ override func visit(_ node: FunctionDeclSyntax) -> SyntaxVisitorContinueKind {
+ switch state {
+ case .topLevel:
+ if let exportedFunction = visitFunction(node: node) {
+ exportedFunctions.append(exportedFunction)
+ }
+ return .skipChildren
+ case .classBody(let name):
+ if let exportedFunction = visitFunction(node: node) {
+ exportedClasses[name]?.methods.append(exportedFunction)
+ }
+ return .skipChildren
+ }
+ }
+
+ private func visitFunction(node: FunctionDeclSyntax) -> ExportedFunction? {
+ guard node.attributes.hasJSAttribute() else {
+ return nil
+ }
+ let name = node.name.text
+ var parameters: [Parameter] = []
+ for param in node.signature.parameterClause.parameters {
+ guard let type = self.parent.lookupType(for: param.type) else {
+ diagnoseUnsupportedType(node: param.type, type: param.type.trimmedDescription)
+ continue
+ }
+ let name = param.secondName?.text ?? param.firstName.text
+ let label = param.firstName.text
+ parameters.append(Parameter(label: label, name: name, type: type))
+ }
+ let returnType: BridgeType
+ if let returnClause = node.signature.returnClause {
+ guard let type = self.parent.lookupType(for: returnClause.type) else {
+ diagnoseUnsupportedType(node: returnClause.type, type: returnClause.type.trimmedDescription)
+ return nil
+ }
+ returnType = type
+ } else {
+ returnType = .void
+ }
+
+ let abiName: String
+ switch state {
+ case .topLevel:
+ abiName = "bjs_\(name)"
+ case .classBody(let className):
+ abiName = "bjs_\(className)_\(name)"
+ }
+
+ return ExportedFunction(
+ name: name,
+ abiName: abiName,
+ parameters: parameters,
+ returnType: returnType
+ )
+ }
+
+ override func visit(_ node: InitializerDeclSyntax) -> SyntaxVisitorContinueKind {
+ guard node.attributes.hasJSAttribute() else { return .skipChildren }
+ guard case .classBody(let name) = state else {
+ diagnose(node: node, message: "@JS init must be inside a @JS class")
+ return .skipChildren
+ }
+ var parameters: [Parameter] = []
+ for param in node.signature.parameterClause.parameters {
+ guard let type = self.parent.lookupType(for: param.type) else {
+ diagnoseUnsupportedType(node: param.type, type: param.type.trimmedDescription)
+ continue
+ }
+ let name = param.secondName?.text ?? param.firstName.text
+ let label = param.firstName.text
+ parameters.append(Parameter(label: label, name: name, type: type))
+ }
+
+ let constructor = ExportedConstructor(
+ abiName: "bjs_\(name)_init",
+ parameters: parameters
+ )
+ exportedClasses[name]?.constructor = constructor
+ return .skipChildren
+ }
+
+ override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
+ let name = node.name.text
+ stateStack.push(state: .classBody(name: name))
+
+ guard node.attributes.hasJSAttribute() else { return .skipChildren }
+ exportedClasses[name] = ExportedClass(
+ name: name,
+ constructor: nil,
+ methods: []
+ )
+ return .visitChildren
+ }
+ override func visitPost(_ node: ClassDeclSyntax) {
+ stateStack.pop()
+ }
+ }
+
+ func parseSingleFile(_ sourceFile: SourceFileSyntax) throws -> [DiagnosticError] {
+ let collector = APICollector(parent: self)
+ collector.walk(sourceFile)
+ exportedFunctions.append(contentsOf: collector.exportedFunctions)
+ exportedClasses.append(contentsOf: collector.exportedClasses.values)
+ return collector.errors
+ }
+
+ func lookupType(for type: TypeSyntax) -> BridgeType? {
+ if let primitive = BridgeType(swiftType: type.trimmedDescription) {
+ return primitive
+ }
+ guard let identifier = type.as(IdentifierTypeSyntax.self) else {
+ return nil
+ }
+ guard let typeDecl = typeDeclResolver.lookupType(for: identifier) else {
+ print("Failed to lookup type \(type.trimmedDescription): not found in typeDeclResolver")
+ return nil
+ }
+ guard typeDecl.is(ClassDeclSyntax.self) || typeDecl.is(ActorDeclSyntax.self) else {
+ print("Failed to lookup type \(type.trimmedDescription): is not a class or actor")
+ return nil
+ }
+ return .swiftHeapObject(typeDecl.name.text)
+ }
+
+ static let prelude: DeclSyntax = """
+ // 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`.
+ @_extern(wasm, module: "bjs", name: "return_string")
+ private func _return_string(_ ptr: UnsafePointer?, _ len: Int32)
+ @_extern(wasm, module: "bjs", name: "init_memory")
+ private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?)
+ """
+
+ func renderSwiftGlue() -> String? {
+ var decls: [DeclSyntax] = []
+ guard exportedFunctions.count > 0 || exportedClasses.count > 0 else {
+ return nil
+ }
+ decls.append(Self.prelude)
+ for function in exportedFunctions {
+ decls.append(renderSingleExportedFunction(function: function))
+ }
+ for klass in exportedClasses {
+ decls.append(contentsOf: renderSingleExportedClass(klass: klass))
+ }
+ let format = BasicFormat()
+ return decls.map { $0.formatted(using: format).description }.joined(separator: "\n\n")
+ }
+
+ class ExportedThunkBuilder {
+ var body: [CodeBlockItemSyntax] = []
+ var abiParameterForwardings: [LabeledExprSyntax] = []
+ var abiParameterSignatures: [(name: String, type: WasmCoreType)] = []
+ var abiReturnType: WasmCoreType?
+
+ func liftParameter(param: Parameter) {
+ switch param.type {
+ case .bool:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.name) == 1")
+ )
+ )
+ abiParameterSignatures.append((param.name, .i32))
+ case .int:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.type.swiftType)(\(raw: param.name))")
+ )
+ )
+ abiParameterSignatures.append((param.name, .i32))
+ case .float:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.name)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .f32))
+ case .double:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.name)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .f64))
+ case .string:
+ let bytesLabel = "\(param.name)Bytes"
+ let lengthLabel = "\(param.name)Len"
+ let prepare: CodeBlockItemSyntax = """
+ let \(raw: param.name) = String(unsafeUninitializedCapacity: Int(\(raw: lengthLabel))) { b in
+ _init_memory(\(raw: bytesLabel), b.baseAddress.unsafelyUnwrapped)
+ return Int(\(raw: lengthLabel))
+ }
+ """
+ body.append(prepare)
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.name)")
+ )
+ )
+ abiParameterSignatures.append((bytesLabel, .i32))
+ abiParameterSignatures.append((lengthLabel, .i32))
+ case .jsObject:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.name)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .i32))
+ case .swiftHeapObject:
+ // UnsafeMutableRawPointer is passed as an i32 pointer
+ let objectExpr: ExprSyntax =
+ "Unmanaged<\(raw: param.type.swiftType)>.fromOpaque(\(raw: param.name)).takeUnretainedValue()"
+ abiParameterForwardings.append(
+ LabeledExprSyntax(label: param.label, expression: objectExpr)
+ )
+ abiParameterSignatures.append((param.name, .pointer))
+ case .void:
+ break
+ }
+ }
+
+ func call(name: String, returnType: BridgeType) {
+ let retMutability = returnType == .string ? "var" : "let"
+ let callExpr: ExprSyntax =
+ "\(raw: name)(\(raw: abiParameterForwardings.map { $0.description }.joined(separator: ", ")))"
+ if returnType == .void {
+ body.append("\(raw: callExpr)")
+ } else {
+ body.append(
+ """
+ \(raw: retMutability) ret = \(raw: callExpr)
+ """
+ )
+ }
+ }
+
+ func callMethod(klassName: String, methodName: String, returnType: BridgeType) {
+ let _selfParam = self.abiParameterForwardings.removeFirst()
+ let retMutability = returnType == .string ? "var" : "let"
+ let callExpr: ExprSyntax =
+ "\(raw: _selfParam).\(raw: methodName)(\(raw: abiParameterForwardings.map { $0.description }.joined(separator: ", ")))"
+ if returnType == .void {
+ body.append("\(raw: callExpr)")
+ } else {
+ body.append(
+ """
+ \(raw: retMutability) ret = \(raw: callExpr)
+ """
+ )
+ }
+ }
+
+ func lowerReturnValue(returnType: BridgeType) {
+ switch returnType {
+ case .void:
+ abiReturnType = nil
+ case .bool:
+ abiReturnType = .i32
+ case .int:
+ abiReturnType = .i32
+ case .float:
+ abiReturnType = .f32
+ case .double:
+ abiReturnType = .f64
+ case .string:
+ abiReturnType = nil
+ case .jsObject:
+ abiReturnType = .i32
+ case .swiftHeapObject:
+ // UnsafeMutableRawPointer is returned as an i32 pointer
+ abiReturnType = .pointer
+ }
+
+ switch returnType {
+ case .void: break
+ case .int, .float, .double:
+ body.append("return \(raw: abiReturnType!.swiftType)(ret)")
+ case .bool:
+ body.append("return Int32(ret ? 1 : 0)")
+ case .string:
+ body.append(
+ """
+ return ret.withUTF8 { ptr in
+ _return_string(ptr.baseAddress, Int32(ptr.count))
+ }
+ """
+ )
+ case .jsObject:
+ body.append(
+ """
+ return ret.id
+ """
+ )
+ case .swiftHeapObject:
+ // Perform a manual retain on the object, which will be balanced by a
+ // release called via FinalizationRegistry
+ body.append(
+ """
+ return Unmanaged.passRetained(ret).toOpaque()
+ """
+ )
+ }
+ }
+
+ func render(abiName: String) -> DeclSyntax {
+ return """
+ @_expose(wasm, "\(raw: abiName)")
+ @_cdecl("\(raw: abiName)")
+ public func _\(raw: abiName)(\(raw: parameterSignature())) -> \(raw: returnSignature()) {
+ \(CodeBlockItemListSyntax(body))
+ }
+ """
+ }
+
+ func parameterSignature() -> String {
+ abiParameterSignatures.map { "\($0.name): \($0.type.swiftType)" }.joined(
+ separator: ", "
+ )
+ }
+
+ func returnSignature() -> String {
+ return abiReturnType?.swiftType ?? "Void"
+ }
+ }
+
+ func renderSingleExportedFunction(function: ExportedFunction) -> DeclSyntax {
+ let builder = ExportedThunkBuilder()
+ for param in function.parameters {
+ builder.liftParameter(param: param)
+ }
+ builder.call(name: function.name, returnType: function.returnType)
+ builder.lowerReturnValue(returnType: function.returnType)
+ return builder.render(abiName: function.abiName)
+ }
+
+ /// # Example
+ ///
+ /// Given the following Swift code:
+ ///
+ /// ```swift
+ /// @JS class Greeter {
+ /// var name: String
+ ///
+ /// @JS init(name: String) {
+ /// self.name = name
+ /// }
+ ///
+ /// @JS func greet() -> String {
+ /// return "Hello, \(name)!"
+ /// }
+ /// }
+ /// ```
+ ///
+ /// The following Swift glue code will be generated:
+ ///
+ /// ```swift
+ /// @_expose(wasm, "bjs_Greeter_init")
+ /// @_cdecl("bjs_Greeter_init")
+ /// public func _bjs_Greeter_init(nameBytes: Int32, nameLen: Int32) -> UnsafeMutableRawPointer {
+ /// let name = String(unsafeUninitializedCapacity: Int(nameLen)) { b in
+ /// _init_memory(nameBytes, b.baseAddress.unsafelyUnwrapped)
+ /// return Int(nameLen)
+ /// }
+ /// let ret = Greeter(name: name)
+ /// return Unmanaged.passRetained(ret).toOpaque()
+ /// }
+ ///
+ /// @_expose(wasm, "bjs_Greeter_greet")
+ /// @_cdecl("bjs_Greeter_greet")
+ /// public func _bjs_Greeter_greet(pointer: UnsafeMutableRawPointer) -> Void {
+ /// let _self = Unmanaged.fromOpaque(pointer).takeUnretainedValue()
+ /// var ret = _self.greet()
+ /// return ret.withUTF8 { ptr in
+ /// _return_string(ptr.baseAddress, Int32(ptr.count))
+ /// }
+ /// }
+ /// @_expose(wasm, "bjs_Greeter_deinit")
+ /// @_cdecl("bjs_Greeter_deinit")
+ /// public func _bjs_Greeter_deinit(pointer: UnsafeMutableRawPointer) {
+ /// Unmanaged.fromOpaque(pointer).release()
+ /// }
+ /// ```
+ func renderSingleExportedClass(klass: ExportedClass) -> [DeclSyntax] {
+ var decls: [DeclSyntax] = []
+ if let constructor = klass.constructor {
+ let builder = ExportedThunkBuilder()
+ for param in constructor.parameters {
+ builder.liftParameter(param: param)
+ }
+ builder.call(name: klass.name, returnType: .swiftHeapObject(klass.name))
+ builder.lowerReturnValue(returnType: .swiftHeapObject(klass.name))
+ decls.append(builder.render(abiName: constructor.abiName))
+ }
+ for method in klass.methods {
+ let builder = ExportedThunkBuilder()
+ builder.liftParameter(
+ param: Parameter(label: nil, name: "_self", type: .swiftHeapObject(klass.name))
+ )
+ for param in method.parameters {
+ builder.liftParameter(param: param)
+ }
+ builder.callMethod(
+ klassName: klass.name,
+ methodName: method.name,
+ returnType: method.returnType
+ )
+ builder.lowerReturnValue(returnType: method.returnType)
+ decls.append(builder.render(abiName: method.abiName))
+ }
+
+ do {
+ decls.append(
+ """
+ @_expose(wasm, "bjs_\(raw: klass.name)_deinit")
+ @_cdecl("bjs_\(raw: klass.name)_deinit")
+ public func _bjs_\(raw: klass.name)_deinit(pointer: UnsafeMutableRawPointer) {
+ Unmanaged<\(raw: klass.name)>.fromOpaque(pointer).release()
+ }
+ """
+ )
+ }
+
+ return decls
+ }
+}
+
+extension AttributeListSyntax {
+ fileprivate func hasJSAttribute() -> Bool {
+ return first(where: {
+ $0.as(AttributeSyntax.self)?.attributeName.trimmedDescription == "JS"
+ }) != nil
+ }
+}
+
+extension BridgeType {
+ init?(swiftType: String) {
+ switch swiftType {
+ case "Int":
+ self = .int
+ case "Float":
+ self = .float
+ case "Double":
+ self = .double
+ case "String":
+ self = .string
+ case "Bool":
+ self = .bool
+ default:
+ return nil
+ }
+ }
+}
+
+extension WasmCoreType {
+ var swiftType: String {
+ switch self {
+ case .i32: return "Int32"
+ case .i64: return "Int64"
+ case .f32: return "Float32"
+ case .f64: return "Float64"
+ case .pointer: return "UnsafeMutableRawPointer"
+ }
+ }
+}
+
+extension BridgeType {
+ var swiftType: String {
+ switch self {
+ case .bool: return "Bool"
+ case .int: return "Int"
+ case .float: return "Float"
+ case .double: return "Double"
+ case .string: return "String"
+ case .jsObject(nil): return "JSObject"
+ case .jsObject(let name?): return name
+ case .swiftHeapObject(let name): return name
+ case .void: return "Void"
+ }
+ }
+}
diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/ImportTS.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/ImportTS.swift
new file mode 100644
index 00000000..a97550bd
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSTool/ImportTS.swift
@@ -0,0 +1,535 @@
+import SwiftBasicFormat
+import SwiftSyntax
+import SwiftSyntaxBuilder
+import Foundation
+
+/// Imports TypeScript declarations and generates Swift bridge code
+///
+/// This struct processes TypeScript definition files (.d.ts) and generates:
+/// 1. Swift code to call the JavaScript functions from Swift
+/// 2. Skeleton files that define the structure of the imported APIs
+///
+/// The generated skeletons will be used by ``BridgeJSLink`` to generate
+/// JavaScript glue code and TypeScript definitions.
+struct ImportTS {
+ let progress: ProgressReporting
+ private(set) var skeleton: ImportedModuleSkeleton
+ private var moduleName: String {
+ skeleton.moduleName
+ }
+
+ init(progress: ProgressReporting, moduleName: String) {
+ self.progress = progress
+ self.skeleton = ImportedModuleSkeleton(moduleName: moduleName, children: [])
+ }
+
+ /// Adds a skeleton to the importer's state
+ mutating func addSkeleton(_ skeleton: ImportedFileSkeleton) {
+ self.skeleton.children.append(skeleton)
+ }
+
+ /// Processes a TypeScript definition file and extracts its API information
+ mutating func addSourceFile(_ sourceFile: String, tsconfigPath: String) throws {
+ let nodePath = try which("node")
+ let ts2skeletonPath = URL(fileURLWithPath: #filePath)
+ .deletingLastPathComponent()
+ .deletingLastPathComponent()
+ .appendingPathComponent("JavaScript")
+ .appendingPathComponent("bin")
+ .appendingPathComponent("ts2skeleton.js")
+ let arguments = [ts2skeletonPath.path, sourceFile, "--project", tsconfigPath]
+
+ progress.print("Running ts2skeleton...")
+ progress.print(" \(([nodePath.path] + arguments).joined(separator: " "))")
+
+ let process = Process()
+ let stdoutPipe = Pipe()
+ nonisolated(unsafe) var stdoutData = Data()
+
+ process.executableURL = nodePath
+ process.arguments = arguments
+ process.standardOutput = stdoutPipe
+
+ stdoutPipe.fileHandleForReading.readabilityHandler = { handle in
+ let data = handle.availableData
+ if data.count > 0 {
+ stdoutData.append(data)
+ }
+ }
+ try process.forwardTerminationSignals {
+ try process.run()
+ process.waitUntilExit()
+ }
+
+ if process.terminationStatus != 0 {
+ throw BridgeJSToolError("ts2skeleton returned \(process.terminationStatus)")
+ }
+ let skeleton = try JSONDecoder().decode(ImportedFileSkeleton.self, from: stdoutData)
+ self.addSkeleton(skeleton)
+ }
+
+ /// Finalizes the import process and generates Swift code
+ func finalize() throws -> String? {
+ var decls: [DeclSyntax] = []
+ for skeleton in self.skeleton.children {
+ for function in skeleton.functions {
+ let thunkDecls = try renderSwiftThunk(function, topLevelDecls: &decls)
+ decls.append(contentsOf: thunkDecls)
+ }
+ for type in skeleton.types {
+ let typeDecls = try renderSwiftType(type, topLevelDecls: &decls)
+ decls.append(contentsOf: typeDecls)
+ }
+ }
+ if decls.isEmpty {
+ // No declarations to import
+ return nil
+ }
+
+ let format = BasicFormat()
+ let allDecls: [DeclSyntax] = [Self.prelude] + decls
+ return allDecls.map { $0.formatted(using: format).description }.joined(separator: "\n\n")
+ }
+
+ class ImportedThunkBuilder {
+ let abiName: String
+ let moduleName: String
+
+ var body: [CodeBlockItemSyntax] = []
+ var abiParameterForwardings: [LabeledExprSyntax] = []
+ var abiParameterSignatures: [(name: String, type: WasmCoreType)] = []
+ var abiReturnType: WasmCoreType?
+
+ init(moduleName: String, abiName: String) {
+ self.moduleName = moduleName
+ self.abiName = abiName
+ }
+
+ func lowerParameter(param: Parameter) throws {
+ switch param.type {
+ case .bool:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("Int32(\(raw: param.name) ? 1 : 0)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .i32))
+ case .int:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.name)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .i32))
+ case .float:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.name)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .f32))
+ case .double:
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: param.name)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .f64))
+ case .string:
+ let stringIdName = "\(param.name)Id"
+ body.append(
+ """
+ var \(raw: param.name) = \(raw: param.name)
+
+ """
+ )
+ body.append(
+ """
+ let \(raw: stringIdName) = \(raw: param.name).withUTF8 { b in
+ _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count))
+ }
+ """
+ )
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("\(raw: stringIdName)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .i32))
+ case .jsObject(_?):
+ abiParameterSignatures.append((param.name, .i32))
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("Int32(bitPattern: \(raw: param.name).this.id)")
+ )
+ )
+ case .jsObject(nil):
+ abiParameterForwardings.append(
+ LabeledExprSyntax(
+ label: param.label,
+ expression: ExprSyntax("Int32(bitPattern: \(raw: param.name).id)")
+ )
+ )
+ abiParameterSignatures.append((param.name, .i32))
+ case .swiftHeapObject(_):
+ throw BridgeJSToolError("swiftHeapObject is not supported in imported signatures")
+ case .void:
+ break
+ }
+ }
+
+ func call(returnType: BridgeType) {
+ let call: ExprSyntax =
+ "\(raw: abiName)(\(raw: abiParameterForwardings.map { $0.description }.joined(separator: ", ")))"
+ if returnType == .void {
+ body.append("\(raw: call)")
+ } else {
+ body.append("let ret = \(raw: call)")
+ }
+ }
+
+ func liftReturnValue(returnType: BridgeType) throws {
+ switch returnType {
+ case .bool:
+ abiReturnType = .i32
+ body.append("return ret == 1")
+ case .int:
+ abiReturnType = .i32
+ body.append("return \(raw: returnType.swiftType)(ret)")
+ case .float:
+ abiReturnType = .f32
+ body.append("return \(raw: returnType.swiftType)(ret)")
+ case .double:
+ abiReturnType = .f64
+ body.append("return \(raw: returnType.swiftType)(ret)")
+ case .string:
+ abiReturnType = .i32
+ body.append(
+ """
+ return String(unsafeUninitializedCapacity: Int(ret)) { b in
+ _init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret))
+ return Int(ret)
+ }
+ """
+ )
+ case .jsObject(let name):
+ abiReturnType = .i32
+ if let name = name {
+ body.append("return \(raw: name)(takingThis: ret)")
+ } else {
+ body.append("return JSObject(id: UInt32(bitPattern: ret))")
+ }
+ case .swiftHeapObject(_):
+ throw BridgeJSToolError("swiftHeapObject is not supported in imported signatures")
+ case .void:
+ break
+ }
+ }
+
+ func assignThis(returnType: BridgeType) {
+ guard case .jsObject = returnType else {
+ preconditionFailure("assignThis can only be called with a jsObject return type")
+ }
+ abiReturnType = .i32
+ body.append("self.this = ret")
+ }
+
+ func renderImportDecl() -> DeclSyntax {
+ return DeclSyntax(
+ FunctionDeclSyntax(
+ attributes: AttributeListSyntax(itemsBuilder: {
+ "@_extern(wasm, module: \"\(raw: moduleName)\", name: \"\(raw: abiName)\")"
+ }).with(\.trailingTrivia, .newline),
+ name: .identifier(abiName),
+ signature: FunctionSignatureSyntax(
+ parameterClause: FunctionParameterClauseSyntax(parametersBuilder: {
+ for param in abiParameterSignatures {
+ FunctionParameterSyntax(
+ firstName: .wildcardToken(),
+ secondName: .identifier(param.name),
+ type: IdentifierTypeSyntax(name: .identifier(param.type.swiftType))
+ )
+ }
+ }),
+ returnClause: ReturnClauseSyntax(
+ arrow: .arrowToken(),
+ type: IdentifierTypeSyntax(name: .identifier(abiReturnType.map { $0.swiftType } ?? "Void"))
+ )
+ )
+ )
+ )
+ }
+
+ func renderThunkDecl(name: String, parameters: [Parameter], returnType: BridgeType) -> DeclSyntax {
+ return DeclSyntax(
+ FunctionDeclSyntax(
+ name: .identifier(name),
+ signature: FunctionSignatureSyntax(
+ parameterClause: FunctionParameterClauseSyntax(parametersBuilder: {
+ for param in parameters {
+ FunctionParameterSyntax(
+ firstName: .wildcardToken(),
+ secondName: .identifier(param.name),
+ colon: .colonToken(),
+ type: IdentifierTypeSyntax(name: .identifier(param.type.swiftType))
+ )
+ }
+ }),
+ returnClause: ReturnClauseSyntax(
+ arrow: .arrowToken(),
+ type: IdentifierTypeSyntax(name: .identifier(returnType.swiftType))
+ )
+ ),
+ body: CodeBlockSyntax {
+ self.renderImportDecl()
+ body
+ }
+ )
+ )
+ }
+
+ func renderConstructorDecl(parameters: [Parameter]) -> DeclSyntax {
+ return DeclSyntax(
+ InitializerDeclSyntax(
+ signature: FunctionSignatureSyntax(
+ parameterClause: FunctionParameterClauseSyntax(
+ parametersBuilder: {
+ for param in parameters {
+ FunctionParameterSyntax(
+ firstName: .wildcardToken(),
+ secondName: .identifier(param.name),
+ type: IdentifierTypeSyntax(name: .identifier(param.type.swiftType))
+ )
+ }
+ }
+ )
+ ),
+ bodyBuilder: {
+ self.renderImportDecl()
+ body
+ }
+ )
+ )
+ }
+ }
+
+ static let prelude: DeclSyntax = """
+ // 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(JSObject_id) import JavaScriptKit
+
+ @_extern(wasm, module: "bjs", name: "make_jsstring")
+ private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+ @_extern(wasm, module: "bjs", name: "init_memory_with_result")
+ private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+ @_extern(wasm, module: "bjs", name: "free_jsobject")
+ private func _free_jsobject(_ ptr: Int32) -> Void
+ """
+
+ func renderSwiftThunk(
+ _ function: ImportedFunctionSkeleton,
+ topLevelDecls: inout [DeclSyntax]
+ ) throws -> [DeclSyntax] {
+ let builder = ImportedThunkBuilder(moduleName: moduleName, abiName: function.abiName(context: nil))
+ for param in function.parameters {
+ try builder.lowerParameter(param: param)
+ }
+ builder.call(returnType: function.returnType)
+ try builder.liftReturnValue(returnType: function.returnType)
+ return [
+ builder.renderThunkDecl(
+ name: function.name,
+ parameters: function.parameters,
+ returnType: function.returnType
+ )
+ .with(\.leadingTrivia, Self.renderDocumentation(documentation: function.documentation))
+ ]
+ }
+
+ func renderSwiftType(_ type: ImportedTypeSkeleton, topLevelDecls: inout [DeclSyntax]) throws -> [DeclSyntax] {
+ let name = type.name
+
+ func renderMethod(method: ImportedFunctionSkeleton) throws -> [DeclSyntax] {
+ let builder = ImportedThunkBuilder(moduleName: moduleName, abiName: method.abiName(context: type))
+ try builder.lowerParameter(param: Parameter(label: nil, name: "self", type: .jsObject(name)))
+ for param in method.parameters {
+ try builder.lowerParameter(param: param)
+ }
+ builder.call(returnType: method.returnType)
+ try builder.liftReturnValue(returnType: method.returnType)
+ return [
+ builder.renderThunkDecl(
+ name: method.name,
+ parameters: method.parameters,
+ returnType: method.returnType
+ )
+ .with(\.leadingTrivia, Self.renderDocumentation(documentation: method.documentation))
+ ]
+ }
+
+ func renderConstructorDecl(constructor: ImportedConstructorSkeleton) throws -> [DeclSyntax] {
+ let builder = ImportedThunkBuilder(moduleName: moduleName, abiName: constructor.abiName(context: type))
+ for param in constructor.parameters {
+ try builder.lowerParameter(param: param)
+ }
+ builder.call(returnType: .jsObject(name))
+ builder.assignThis(returnType: .jsObject(name))
+ return [
+ builder.renderConstructorDecl(parameters: constructor.parameters)
+ ]
+ }
+
+ func renderGetterDecl(property: ImportedPropertySkeleton) throws -> AccessorDeclSyntax {
+ let builder = ImportedThunkBuilder(
+ moduleName: moduleName,
+ abiName: property.getterAbiName(context: type)
+ )
+ try builder.lowerParameter(param: Parameter(label: nil, name: "self", type: .jsObject(name)))
+ builder.call(returnType: property.type)
+ try builder.liftReturnValue(returnType: property.type)
+ return AccessorDeclSyntax(
+ accessorSpecifier: .keyword(.get),
+ body: CodeBlockSyntax {
+ builder.renderImportDecl()
+ builder.body
+ }
+ )
+ }
+
+ func renderSetterDecl(property: ImportedPropertySkeleton) throws -> AccessorDeclSyntax {
+ let builder = ImportedThunkBuilder(
+ moduleName: moduleName,
+ abiName: property.setterAbiName(context: type)
+ )
+ try builder.lowerParameter(param: Parameter(label: nil, name: "self", type: .jsObject(name)))
+ try builder.lowerParameter(param: Parameter(label: nil, name: "newValue", type: property.type))
+ builder.call(returnType: .void)
+ return AccessorDeclSyntax(
+ modifier: DeclModifierSyntax(name: .keyword(.nonmutating)),
+ accessorSpecifier: .keyword(.set),
+ body: CodeBlockSyntax {
+ builder.renderImportDecl()
+ builder.body
+ }
+ )
+ }
+
+ func renderPropertyDecl(property: ImportedPropertySkeleton) throws -> [DeclSyntax] {
+ var accessorDecls: [AccessorDeclSyntax] = []
+ accessorDecls.append(try renderGetterDecl(property: property))
+ if !property.isReadonly {
+ accessorDecls.append(try renderSetterDecl(property: property))
+ }
+ return [
+ DeclSyntax(
+ VariableDeclSyntax(
+ leadingTrivia: Self.renderDocumentation(documentation: property.documentation),
+ bindingSpecifier: .keyword(.var),
+ bindingsBuilder: {
+ PatternBindingListSyntax {
+ PatternBindingSyntax(
+ pattern: IdentifierPatternSyntax(identifier: .identifier(property.name)),
+ typeAnnotation: TypeAnnotationSyntax(
+ type: IdentifierTypeSyntax(name: .identifier(property.type.swiftType))
+ ),
+ accessorBlock: AccessorBlockSyntax(
+ accessors: .accessors(
+ AccessorDeclListSyntax(accessorDecls)
+ )
+ )
+ )
+ }
+ }
+ )
+ )
+ ]
+ }
+ let classDecl = try StructDeclSyntax(
+ leadingTrivia: Self.renderDocumentation(documentation: type.documentation),
+ name: .identifier(name),
+ memberBlockBuilder: {
+ DeclSyntax(
+ """
+ let this: JSObject
+ """
+ ).with(\.trailingTrivia, .newlines(2))
+
+ DeclSyntax(
+ """
+ init(this: JSObject) {
+ self.this = this
+ }
+ """
+ ).with(\.trailingTrivia, .newlines(2))
+
+ DeclSyntax(
+ """
+ init(takingThis this: Int32) {
+ self.this = JSObject(id: UInt32(bitPattern: this))
+ }
+ """
+ ).with(\.trailingTrivia, .newlines(2))
+
+ if let constructor = type.constructor {
+ try renderConstructorDecl(constructor: constructor).map { $0.with(\.trailingTrivia, .newlines(2)) }
+ }
+
+ for property in type.properties {
+ try renderPropertyDecl(property: property).map { $0.with(\.trailingTrivia, .newlines(2)) }
+ }
+
+ for method in type.methods {
+ try renderMethod(method: method).map { $0.with(\.trailingTrivia, .newlines(2)) }
+ }
+ }
+ )
+
+ return [DeclSyntax(classDecl)]
+ }
+
+ static func renderDocumentation(documentation: String?) -> Trivia {
+ guard let documentation = documentation else {
+ return Trivia()
+ }
+ let lines = documentation.split { $0.isNewline }
+ return Trivia(pieces: lines.flatMap { [TriviaPiece.docLineComment("/// \($0)"), .newlines(1)] })
+ }
+}
+
+extension Foundation.Process {
+ // Monitor termination/interrruption signals to forward them to child process
+ func setSignalForwarding(_ signalNo: Int32) -> DispatchSourceSignal {
+ let signalSource = DispatchSource.makeSignalSource(signal: signalNo)
+ signalSource.setEventHandler { [self] in
+ signalSource.cancel()
+ kill(processIdentifier, signalNo)
+ }
+ signalSource.resume()
+ return signalSource
+ }
+
+ func forwardTerminationSignals(_ body: () throws -> Void) rethrows {
+ let sources = [
+ setSignalForwarding(SIGINT),
+ setSignalForwarding(SIGTERM),
+ ]
+ defer {
+ for source in sources {
+ source.cancel()
+ }
+ }
+ try body()
+ }
+}
diff --git a/Plugins/BridgeJS/Sources/BridgeJSTool/TypeDeclResolver.swift b/Plugins/BridgeJS/Sources/BridgeJSTool/TypeDeclResolver.swift
new file mode 100644
index 00000000..a7b183af
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/BridgeJSTool/TypeDeclResolver.swift
@@ -0,0 +1,112 @@
+import SwiftSyntax
+
+/// Resolves type declarations from Swift syntax nodes
+class TypeDeclResolver {
+ typealias TypeDecl = NamedDeclSyntax & DeclGroupSyntax & DeclSyntaxProtocol
+ /// A representation of a qualified name of a type declaration
+ ///
+ /// `Outer.Inner` type declaration is represented as ["Outer", "Inner"]
+ typealias QualifiedName = [String]
+ private var typeDeclByQualifiedName: [QualifiedName: TypeDecl] = [:]
+
+ enum Error: Swift.Error {
+ case typeNotFound(QualifiedName)
+ }
+
+ private class TypeDeclCollector: SyntaxVisitor {
+ let resolver: TypeDeclResolver
+ var scope: [TypeDecl] = []
+ var rootTypeDecls: [TypeDecl] = []
+
+ init(resolver: TypeDeclResolver) {
+ self.resolver = resolver
+ super.init(viewMode: .all)
+ }
+
+ func visitNominalDecl(_ node: TypeDecl) -> SyntaxVisitorContinueKind {
+ let name = node.name.text
+ let qualifiedName = scope.map(\.name.text) + [name]
+ resolver.typeDeclByQualifiedName[qualifiedName] = node
+ scope.append(node)
+ return .visitChildren
+ }
+
+ func visitPostNominalDecl() {
+ let type = scope.removeLast()
+ if scope.isEmpty {
+ rootTypeDecls.append(type)
+ }
+ }
+
+ override func visit(_ node: StructDeclSyntax) -> SyntaxVisitorContinueKind {
+ return visitNominalDecl(node)
+ }
+ override func visit(_ node: ClassDeclSyntax) -> SyntaxVisitorContinueKind {
+ return visitNominalDecl(node)
+ }
+ override func visitPost(_ node: ClassDeclSyntax) {
+ visitPostNominalDecl()
+ }
+ override func visit(_ node: ActorDeclSyntax) -> SyntaxVisitorContinueKind {
+ return visitNominalDecl(node)
+ }
+ override func visitPost(_ node: ActorDeclSyntax) {
+ visitPostNominalDecl()
+ }
+ override func visitPost(_ node: StructDeclSyntax) {
+ visitPostNominalDecl()
+ }
+ override func visit(_ node: EnumDeclSyntax) -> SyntaxVisitorContinueKind {
+ return visitNominalDecl(node)
+ }
+ override func visitPost(_ node: EnumDeclSyntax) {
+ visitPostNominalDecl()
+ }
+ }
+
+ /// Collects type declarations from a parsed Swift source file
+ func addSourceFile(_ sourceFile: SourceFileSyntax) {
+ let collector = TypeDeclCollector(resolver: self)
+ collector.walk(sourceFile)
+ }
+
+ /// Builds the type name scope for a given type usage
+ private func buildScope(type: IdentifierTypeSyntax) -> QualifiedName {
+ var innerToOuter: [String] = []
+ var context: SyntaxProtocol = type
+ while let parent = context.parent {
+ if let parent = parent.asProtocol(NamedDeclSyntax.self), parent.isProtocol(DeclGroupSyntax.self) {
+ innerToOuter.append(parent.name.text)
+ }
+ context = parent
+ }
+ return innerToOuter.reversed()
+ }
+
+ /// Looks up a qualified name of a type declaration by its unqualified type usage
+ /// Returns the qualified name hierarchy of the type declaration
+ /// If the type declaration is not found, returns the unqualified name
+ private func tryQualify(type: IdentifierTypeSyntax) -> QualifiedName {
+ let name = type.name.text
+ let scope = buildScope(type: type)
+ /// Search for the type declaration from the innermost scope to the outermost scope
+ for i in (0...scope.count).reversed() {
+ let qualifiedName = Array(scope[0.. TypeDecl? {
+ let qualifiedName = tryQualify(type: type)
+ return typeDeclByQualifiedName[qualifiedName]
+ }
+
+ /// Looks up a type declaration by its fully qualified name
+ func lookupType(fullyQualified: QualifiedName) -> TypeDecl? {
+ return typeDeclByQualifiedName[fullyQualified]
+ }
+}
diff --git a/Plugins/BridgeJS/Sources/JavaScript/README.md b/Plugins/BridgeJS/Sources/JavaScript/README.md
new file mode 100644
index 00000000..de680635
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/JavaScript/README.md
@@ -0,0 +1,3 @@
+# ts2skeleton
+
+This script analyzes the TypeScript type definitions and produces a structured JSON output with skeleton information that can be used to generate Swift bindings.
diff --git a/Plugins/BridgeJS/Sources/JavaScript/bin/ts2skeleton.js b/Plugins/BridgeJS/Sources/JavaScript/bin/ts2skeleton.js
new file mode 100755
index 00000000..ba926a88
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/JavaScript/bin/ts2skeleton.js
@@ -0,0 +1,14 @@
+#!/usr/bin/env node
+// @ts-check
+
+/**
+ * Main entry point for the ts2skeleton tool
+ *
+ * This script analyzes the TypeScript type definitions and produces a structured
+ * JSON output with skeleton information that can be used to generate Swift
+ * bindings.
+ */
+
+import { main } from "../src/cli.js"
+
+main(process.argv.slice(2));
diff --git a/Plugins/BridgeJS/Sources/JavaScript/package.json b/Plugins/BridgeJS/Sources/JavaScript/package.json
new file mode 100644
index 00000000..48fb77cf
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/JavaScript/package.json
@@ -0,0 +1,9 @@
+{
+ "type": "module",
+ "dependencies": {
+ "typescript": "5.8.2"
+ },
+ "bin": {
+ "ts2skeleton": "./bin/ts2skeleton.js"
+ }
+}
diff --git a/Plugins/BridgeJS/Sources/JavaScript/src/cli.js b/Plugins/BridgeJS/Sources/JavaScript/src/cli.js
new file mode 100644
index 00000000..6d2a1ed8
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/JavaScript/src/cli.js
@@ -0,0 +1,139 @@
+// @ts-check
+import * as fs from 'fs';
+import { TypeProcessor } from './processor.js';
+import { parseArgs } from 'util';
+import ts from 'typescript';
+import path from 'path';
+
+class DiagnosticEngine {
+ constructor() {
+ /** @type {ts.FormatDiagnosticsHost} */
+ this.formattHost = {
+ getCanonicalFileName: (fileName) => fileName,
+ getNewLine: () => ts.sys.newLine,
+ getCurrentDirectory: () => ts.sys.getCurrentDirectory(),
+ };
+ }
+
+ /**
+ * @param {readonly ts.Diagnostic[]} diagnostics
+ */
+ tsDiagnose(diagnostics) {
+ const message = ts.formatDiagnosticsWithColorAndContext(diagnostics, this.formattHost);
+ console.log(message);
+ }
+
+ /**
+ * @param {string} message
+ * @param {ts.Node | undefined} node
+ */
+ info(message, node = undefined) {
+ this.printLog("info", '\x1b[32m', message, node);
+ }
+
+ /**
+ * @param {string} message
+ * @param {ts.Node | undefined} node
+ */
+ warn(message, node = undefined) {
+ this.printLog("warning", '\x1b[33m', message, node);
+ }
+
+ /**
+ * @param {string} message
+ */
+ error(message) {
+ this.printLog("error", '\x1b[31m', message);
+ }
+
+ /**
+ * @param {string} level
+ * @param {string} color
+ * @param {string} message
+ * @param {ts.Node | undefined} node
+ */
+ printLog(level, color, message, node = undefined) {
+ if (node) {
+ const sourceFile = node.getSourceFile();
+ const { line, character } = sourceFile.getLineAndCharacterOfPosition(node.getStart());
+ const location = sourceFile.fileName + ":" + (line + 1) + ":" + (character);
+ process.stderr.write(`${location}: ${color}${level}\x1b[0m: ${message}\n`);
+ } else {
+ process.stderr.write(`${color}${level}\x1b[0m: ${message}\n`);
+ }
+ }
+}
+
+function printUsage() {
+ console.error('Usage: ts2skeleton -p [-o output.json]');
+}
+
+/**
+ * Main function to run the CLI
+ * @param {string[]} args - Command-line arguments
+ * @returns {void}
+ */
+export function main(args) {
+ // Parse command line arguments
+ const options = parseArgs({
+ args,
+ options: {
+ output: {
+ type: 'string',
+ short: 'o',
+ },
+ project: {
+ type: 'string',
+ short: 'p',
+ }
+ },
+ allowPositionals: true
+ })
+
+ if (options.positionals.length !== 1) {
+ printUsage();
+ process.exit(1);
+ }
+
+ const tsconfigPath = options.values.project;
+ if (!tsconfigPath) {
+ printUsage();
+ process.exit(1);
+ }
+
+ const filePath = options.positionals[0];
+ const diagnosticEngine = new DiagnosticEngine();
+
+ diagnosticEngine.info(`Processing ${filePath}...`);
+
+ // Create TypeScript program and process declarations
+ const configFile = ts.readConfigFile(tsconfigPath, ts.sys.readFile);
+ const configParseResult = ts.parseJsonConfigFileContent(
+ configFile.config,
+ ts.sys,
+ path.dirname(path.resolve(tsconfigPath))
+ );
+
+ if (configParseResult.errors.length > 0) {
+ diagnosticEngine.tsDiagnose(configParseResult.errors);
+ process.exit(1);
+ }
+
+ const program = TypeProcessor.createProgram(filePath, configParseResult.options);
+ const diagnostics = program.getSemanticDiagnostics();
+ if (diagnostics.length > 0) {
+ diagnosticEngine.tsDiagnose(diagnostics);
+ process.exit(1);
+ }
+
+ const processor = new TypeProcessor(program.getTypeChecker(), diagnosticEngine);
+ const results = processor.processTypeDeclarations(program, filePath);
+
+ // Write results to file or stdout
+ const jsonOutput = JSON.stringify(results, null, 2);
+ if (options.values.output) {
+ fs.writeFileSync(options.values.output, jsonOutput);
+ } else {
+ process.stdout.write(jsonOutput, "utf-8");
+ }
+}
diff --git a/Plugins/BridgeJS/Sources/JavaScript/src/index.d.ts b/Plugins/BridgeJS/Sources/JavaScript/src/index.d.ts
new file mode 100644
index 00000000..e1daa4af
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/JavaScript/src/index.d.ts
@@ -0,0 +1,44 @@
+export type BridgeType =
+ | { "int": {} }
+ | { "float": {} }
+ | { "double": {} }
+ | { "string": {} }
+ | { "bool": {} }
+ | { "jsObject": { "_0": string } | {} }
+ | { "void": {} }
+
+export type Parameter = {
+ name: string;
+ type: BridgeType;
+}
+
+export type ImportFunctionSkeleton = {
+ name: string;
+ parameters: Parameter[];
+ returnType: BridgeType;
+ documentation: string | undefined;
+}
+
+export type ImportConstructorSkeleton = {
+ parameters: Parameter[];
+}
+
+export type ImportPropertySkeleton = {
+ name: string;
+ type: BridgeType;
+ isReadonly: boolean;
+ documentation: string | undefined;
+}
+
+export type ImportTypeSkeleton = {
+ name: string;
+ documentation: string | undefined;
+ constructor?: ImportConstructorSkeleton;
+ properties: ImportPropertySkeleton[];
+ methods: ImportFunctionSkeleton[];
+}
+
+export type ImportSkeleton = {
+ functions: ImportFunctionSkeleton[];
+ types: ImportTypeSkeleton[];
+}
diff --git a/Plugins/BridgeJS/Sources/JavaScript/src/processor.js b/Plugins/BridgeJS/Sources/JavaScript/src/processor.js
new file mode 100644
index 00000000..e3887b3c
--- /dev/null
+++ b/Plugins/BridgeJS/Sources/JavaScript/src/processor.js
@@ -0,0 +1,414 @@
+/**
+ * TypeScript type processing functionality
+ * @module processor
+ */
+
+// @ts-check
+import ts from 'typescript';
+
+/** @typedef {import('./index').ImportSkeleton} ImportSkeleton */
+/** @typedef {import('./index').ImportFunctionSkeleton} ImportFunctionSkeleton */
+/** @typedef {import('./index').ImportTypeSkeleton} ImportTypeSkeleton */
+/** @typedef {import('./index').ImportPropertySkeleton} ImportPropertySkeleton */
+/** @typedef {import('./index').ImportConstructorSkeleton} ImportConstructorSkeleton */
+/** @typedef {import('./index').Parameter} Parameter */
+/** @typedef {import('./index').BridgeType} BridgeType */
+
+/**
+ * @typedef {{
+ * warn: (message: string, node?: ts.Node) => void,
+ * error: (message: string, node?: ts.Node) => void,
+ * }} DiagnosticEngine
+ */
+
+/**
+ * TypeScript type processor class
+ */
+export class TypeProcessor {
+ /**
+ * Create a TypeScript program from a d.ts file
+ * @param {string} filePath - Path to the d.ts file
+ * @param {ts.CompilerOptions} options - Compiler options
+ * @returns {ts.Program} TypeScript program object
+ */
+ static createProgram(filePath, options) {
+ const host = ts.createCompilerHost(options);
+ return ts.createProgram([filePath], options, host);
+ }
+
+ /**
+ * @param {ts.TypeChecker} checker - TypeScript type checker
+ * @param {DiagnosticEngine} diagnosticEngine - Diagnostic engine
+ */
+ constructor(checker, diagnosticEngine, options = {
+ inheritIterable: true,
+ inheritArraylike: true,
+ inheritPromiselike: true,
+ addAllParentMembersToClass: true,
+ replaceAliasToFunction: true,
+ replaceRankNFunction: true,
+ replaceNewableFunction: true,
+ noExtendsInTyprm: false,
+ }) {
+ this.checker = checker;
+ this.diagnosticEngine = diagnosticEngine;
+ this.options = options;
+
+ /** @type {Map} */
+ this.processedTypes = new Map();
+ /** @type {Map} Seen position by type */
+ this.seenTypes = new Map();
+ /** @type {ImportFunctionSkeleton[]} */
+ this.functions = [];
+ /** @type {ImportTypeSkeleton[]} */
+ this.types = [];
+ }
+
+ /**
+ * Process type declarations from a TypeScript program
+ * @param {ts.Program} program - TypeScript program
+ * @param {string} inputFilePath - Path to the input file
+ * @returns {ImportSkeleton} Processed type declarations
+ */
+ processTypeDeclarations(program, inputFilePath) {
+ const sourceFiles = program.getSourceFiles().filter(
+ sf => !sf.isDeclarationFile || sf.fileName === inputFilePath
+ );
+
+ for (const sourceFile of sourceFiles) {
+ if (sourceFile.fileName.includes('node_modules/typescript/lib')) continue;
+
+ Error.stackTraceLimit = 100;
+
+ try {
+ sourceFile.forEachChild(node => {
+ this.visitNode(node);
+
+ for (const [type, node] of this.seenTypes) {
+ this.seenTypes.delete(type);
+ const typeString = this.checker.typeToString(type);
+ const members = type.getProperties();
+ if (members) {
+ const type = this.visitStructuredType(typeString, members);
+ this.types.push(type);
+ } else {
+ this.types.push(this.createUnknownType(typeString));
+ }
+ }
+ });
+ } catch (error) {
+ this.diagnosticEngine.error(`Error processing ${sourceFile.fileName}: ${error.message}`);
+ }
+ }
+
+ return { functions: this.functions, types: this.types };
+ }
+
+ /**
+ * Create an unknown type
+ * @param {string} typeString - Type string
+ * @returns {ImportTypeSkeleton} Unknown type
+ */
+ createUnknownType(typeString) {
+ return {
+ name: typeString,
+ documentation: undefined,
+ properties: [],
+ methods: [],
+ constructor: undefined,
+ };
+ }
+
+ /**
+ * Visit a node and process it
+ * @param {ts.Node} node - The node to visit
+ */
+ visitNode(node) {
+ if (ts.isFunctionDeclaration(node)) {
+ const func = this.visitFunctionLikeDecl(node);
+ if (func && node.name) {
+ this.functions.push({ ...func, name: node.name.getText() });
+ }
+ } else if (ts.isClassDeclaration(node)) {
+ const cls = this.visitClassDecl(node);
+ if (cls) this.types.push(cls);
+ }
+ }
+
+ /**
+ * Process a function declaration into ImportFunctionSkeleton format
+ * @param {ts.SignatureDeclaration} node - The function node
+ * @returns {ImportFunctionSkeleton | null} Processed function
+ * @private
+ */
+ visitFunctionLikeDecl(node) {
+ if (!node.name) return null;
+
+ const signature = this.checker.getSignatureFromDeclaration(node);
+ if (!signature) return null;
+
+ /** @type {Parameter[]} */
+ const parameters = [];
+ for (const p of signature.getParameters()) {
+ const bridgeType = this.visitSignatureParameter(p, node);
+ parameters.push(bridgeType);
+ }
+
+ const returnType = signature.getReturnType();
+ const bridgeReturnType = this.visitType(returnType, node);
+ const documentation = this.getFullJSDocText(node);
+
+ return {
+ name: node.name.getText(),
+ parameters,
+ returnType: bridgeReturnType,
+ documentation,
+ };
+ }
+
+ /**
+ * Get the full JSDoc text from a node
+ * @param {ts.Node} node - The node to get the JSDoc text from
+ * @returns {string | undefined} The full JSDoc text
+ */
+ getFullJSDocText(node) {
+ const docs = ts.getJSDocCommentsAndTags(node);
+ const parts = [];
+ for (const doc of docs) {
+ if (ts.isJSDoc(doc)) {
+ parts.push(doc.comment ?? "");
+ }
+ }
+ if (parts.length === 0) return undefined;
+ return parts.join("\n");
+ }
+
+ /**
+ * @param {ts.ConstructorDeclaration} node
+ * @returns {ImportConstructorSkeleton | null}
+ */
+ visitConstructorDecl(node) {
+ const signature = this.checker.getSignatureFromDeclaration(node);
+ if (!signature) return null;
+
+ const parameters = [];
+ for (const p of signature.getParameters()) {
+ const bridgeType = this.visitSignatureParameter(p, node);
+ parameters.push(bridgeType);
+ }
+
+ return { parameters };
+ }
+
+ /**
+ * @param {ts.PropertyDeclaration | ts.PropertySignature} node
+ * @returns {ImportPropertySkeleton | null}
+ */
+ visitPropertyDecl(node) {
+ if (!node.name) return null;
+ const type = this.checker.getTypeAtLocation(node)
+ const bridgeType = this.visitType(type, node);
+ const isReadonly = node.modifiers?.some(m => m.kind === ts.SyntaxKind.ReadonlyKeyword) ?? false;
+ const documentation = this.getFullJSDocText(node);
+ return { name: node.name.getText(), type: bridgeType, isReadonly, documentation };
+ }
+
+ /**
+ * @param {ts.Symbol} symbol
+ * @param {ts.Node} node
+ * @returns {Parameter}
+ */
+ visitSignatureParameter(symbol, node) {
+ const type = this.checker.getTypeOfSymbolAtLocation(symbol, node);
+ const bridgeType = this.visitType(type, node);
+ return { name: symbol.name, type: bridgeType };
+ }
+
+ /**
+ * @param {ts.ClassDeclaration} node
+ * @returns {ImportTypeSkeleton | null}
+ */
+ visitClassDecl(node) {
+ if (!node.name) return null;
+
+ const name = node.name.text;
+ const properties = [];
+ const methods = [];
+ /** @type {ImportConstructorSkeleton | undefined} */
+ let constructor = undefined;
+
+ for (const member of node.members) {
+ if (ts.isPropertyDeclaration(member)) {
+ // TODO
+ } else if (ts.isMethodDeclaration(member)) {
+ const decl = this.visitFunctionLikeDecl(member);
+ if (decl) methods.push(decl);
+ } else if (ts.isConstructorDeclaration(member)) {
+ const decl = this.visitConstructorDecl(member);
+ if (decl) constructor = decl;
+ }
+ }
+
+ const documentation = this.getFullJSDocText(node);
+ return {
+ name,
+ constructor,
+ properties,
+ methods,
+ documentation,
+ };
+ }
+
+ /**
+ * @param {ts.SymbolFlags} flags
+ * @returns {string[]}
+ */
+ debugSymbolFlags(flags) {
+ const result = [];
+ for (const key in ts.SymbolFlags) {
+ const val = (ts.SymbolFlags)[key];
+ if (typeof val === "number" && (flags & val) !== 0) {
+ result.push(key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @param {ts.TypeFlags} flags
+ * @returns {string[]}
+ */
+ debugTypeFlags(flags) {
+ const result = [];
+ for (const key in ts.TypeFlags) {
+ const val = (ts.TypeFlags)[key];
+ if (typeof val === "number" && (flags & val) !== 0) {
+ result.push(key);
+ }
+ }
+ return result;
+ }
+
+ /**
+ * @param {string} name
+ * @param {ts.Symbol[]} members
+ * @returns {ImportTypeSkeleton}
+ */
+ visitStructuredType(name, members) {
+ /** @type {ImportPropertySkeleton[]} */
+ const properties = [];
+ /** @type {ImportFunctionSkeleton[]} */
+ const methods = [];
+ /** @type {ImportConstructorSkeleton | undefined} */
+ let constructor = undefined;
+ for (const symbol of members) {
+ if (symbol.flags & ts.SymbolFlags.Property) {
+ for (const decl of symbol.getDeclarations() ?? []) {
+ if (ts.isPropertyDeclaration(decl) || ts.isPropertySignature(decl)) {
+ const property = this.visitPropertyDecl(decl);
+ if (property) properties.push(property);
+ } else if (ts.isMethodSignature(decl)) {
+ const method = this.visitFunctionLikeDecl(decl);
+ if (method) methods.push(method);
+ }
+ }
+ } else if (symbol.flags & ts.SymbolFlags.Method) {
+ for (const decl of symbol.getDeclarations() ?? []) {
+ if (!ts.isMethodSignature(decl)) {
+ continue;
+ }
+ const method = this.visitFunctionLikeDecl(decl);
+ if (method) methods.push(method);
+ }
+ } else if (symbol.flags & ts.SymbolFlags.Constructor) {
+ for (const decl of symbol.getDeclarations() ?? []) {
+ if (!ts.isConstructorDeclaration(decl)) {
+ continue;
+ }
+ const ctor = this.visitConstructorDecl(decl);
+ if (ctor) constructor = ctor;
+ }
+ }
+ }
+ return { name, properties, methods, constructor, documentation: undefined };
+ }
+
+ /**
+ * Convert TypeScript type string to BridgeType
+ * @param {ts.Type} type - TypeScript type string
+ * @param {ts.Node} node - Node
+ * @returns {BridgeType} Bridge type
+ * @private
+ */
+ visitType(type, node) {
+ const maybeProcessed = this.processedTypes.get(type);
+ if (maybeProcessed) {
+ return maybeProcessed;
+ }
+ /**
+ * @param {ts.Type} type
+ * @returns {BridgeType}
+ */
+ const convert = (type) => {
+ /** @type {Record} */
+ const typeMap = {
+ "number": { "double": {} },
+ "string": { "string": {} },
+ "boolean": { "bool": {} },
+ "void": { "void": {} },
+ "any": { "jsObject": {} },
+ "unknown": { "jsObject": {} },
+ "null": { "void": {} },
+ "undefined": { "void": {} },
+ "bigint": { "int": {} },
+ "object": { "jsObject": {} },
+ "symbol": { "jsObject": {} },
+ "never": { "void": {} },
+ };
+ const typeString = this.checker.typeToString(type);
+ if (typeMap[typeString]) {
+ return typeMap[typeString];
+ }
+
+ if (this.checker.isArrayType(type) || this.checker.isTupleType(type) || type.getCallSignatures().length > 0) {
+ return { "jsObject": {} };
+ }
+ // "a" | "b" -> string
+ if (this.checker.isTypeAssignableTo(type, this.checker.getStringType())) {
+ return { "string": {} };
+ }
+ if (type.getFlags() & ts.TypeFlags.TypeParameter) {
+ return { "jsObject": {} };
+ }
+
+ const typeName = this.deriveTypeName(type);
+ if (!typeName) {
+ this.diagnosticEngine.warn(`Unknown non-nominal type: ${typeString}`, node);
+ return { "jsObject": {} };
+ }
+ this.seenTypes.set(type, node);
+ return { "jsObject": { "_0": typeName } };
+ }
+ const bridgeType = convert(type);
+ this.processedTypes.set(type, bridgeType);
+ return bridgeType;
+ }
+
+ /**
+ * Derive the type name from a type
+ * @param {ts.Type} type - TypeScript type
+ * @returns {string | undefined} Type name
+ * @private
+ */
+ deriveTypeName(type) {
+ const aliasSymbol = type.aliasSymbol;
+ if (aliasSymbol) {
+ return aliasSymbol.name;
+ }
+ const typeSymbol = type.getSymbol();
+ if (typeSymbol) {
+ return typeSymbol.name;
+ }
+ return undefined;
+ }
+}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift
new file mode 100644
index 00000000..e052ed42
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/BridgeJSLinkTests.swift
@@ -0,0 +1,80 @@
+import Foundation
+import SwiftSyntax
+import SwiftParser
+import Testing
+
+@testable import BridgeJSLink
+@testable import BridgeJSTool
+
+@Suite struct BridgeJSLinkTests {
+ private func snapshot(
+ bridgeJSLink: BridgeJSLink,
+ name: String? = nil,
+ filePath: String = #filePath,
+ function: String = #function,
+ sourceLocation: Testing.SourceLocation = #_sourceLocation
+ ) throws {
+ let (outputJs, outputDts) = try bridgeJSLink.link()
+ try assertSnapshot(
+ name: name,
+ filePath: filePath,
+ function: function,
+ sourceLocation: sourceLocation,
+ input: outputJs.data(using: .utf8)!,
+ fileExtension: "js"
+ )
+ try assertSnapshot(
+ name: name,
+ filePath: filePath,
+ function: function,
+ sourceLocation: sourceLocation,
+ input: outputDts.data(using: .utf8)!,
+ fileExtension: "d.ts"
+ )
+ }
+
+ static let inputsDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent(
+ "Inputs"
+ )
+
+ static func collectInputs(extension: String) -> [String] {
+ let fileManager = FileManager.default
+ let inputs = try! fileManager.contentsOfDirectory(atPath: Self.inputsDirectory.path)
+ return inputs.filter { $0.hasSuffix(`extension`) }
+ }
+
+ @Test(arguments: collectInputs(extension: ".swift"))
+ func snapshotExport(input: String) throws {
+ let url = Self.inputsDirectory.appendingPathComponent(input)
+ let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8))
+ let swiftAPI = ExportSwift(progress: .silent)
+ try swiftAPI.addSourceFile(sourceFile, input)
+ let name = url.deletingPathExtension().lastPathComponent
+
+ let (_, outputSkeleton) = try #require(try swiftAPI.finalize())
+ let encoder = JSONEncoder()
+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
+ let outputSkeletonData = try encoder.encode(outputSkeleton)
+ var bridgeJSLink = BridgeJSLink()
+ try bridgeJSLink.addExportedSkeletonFile(data: outputSkeletonData)
+ try snapshot(bridgeJSLink: bridgeJSLink, name: name + ".Export")
+ }
+
+ @Test(arguments: collectInputs(extension: ".d.ts"))
+ func snapshotImport(input: String) throws {
+ let url = Self.inputsDirectory.appendingPathComponent(input)
+ let tsconfigPath = url.deletingLastPathComponent().appendingPathComponent("tsconfig.json")
+
+ var importTS = ImportTS(progress: .silent, moduleName: "TestModule")
+ try importTS.addSourceFile(url.path, tsconfigPath: tsconfigPath.path)
+ let name = url.deletingPathExtension().deletingPathExtension().lastPathComponent
+
+ let encoder = JSONEncoder()
+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
+ let outputSkeletonData = try encoder.encode(importTS.skeleton)
+
+ var bridgeJSLink = BridgeJSLink()
+ try bridgeJSLink.addImportedSkeletonFile(data: outputSkeletonData)
+ try snapshot(bridgeJSLink: bridgeJSLink, name: name + ".Import")
+ }
+}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/ExportSwiftTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/ExportSwiftTests.swift
new file mode 100644
index 00000000..6064bb28
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/ExportSwiftTests.swift
@@ -0,0 +1,57 @@
+import Foundation
+import SwiftSyntax
+import SwiftParser
+import Testing
+
+@testable import BridgeJSTool
+
+@Suite struct ExportSwiftTests {
+ private func snapshot(
+ swiftAPI: ExportSwift,
+ name: String? = nil,
+ filePath: String = #filePath,
+ function: String = #function,
+ sourceLocation: Testing.SourceLocation = #_sourceLocation
+ ) throws {
+ let (outputSwift, outputSkeleton) = try #require(try swiftAPI.finalize())
+ try assertSnapshot(
+ name: name,
+ filePath: filePath,
+ function: function,
+ sourceLocation: sourceLocation,
+ input: outputSwift.data(using: .utf8)!,
+ fileExtension: "swift"
+ )
+ let encoder = JSONEncoder()
+ encoder.outputFormatting = [.prettyPrinted, .sortedKeys]
+ let outputSkeletonData = try encoder.encode(outputSkeleton)
+ try assertSnapshot(
+ name: name,
+ filePath: filePath,
+ function: function,
+ sourceLocation: sourceLocation,
+ input: outputSkeletonData,
+ fileExtension: "json"
+ )
+ }
+
+ static let inputsDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent(
+ "Inputs"
+ )
+
+ static func collectInputs() -> [String] {
+ let fileManager = FileManager.default
+ let inputs = try! fileManager.contentsOfDirectory(atPath: Self.inputsDirectory.path)
+ return inputs.filter { $0.hasSuffix(".swift") }
+ }
+
+ @Test(arguments: collectInputs())
+ func snapshot(input: String) throws {
+ let swiftAPI = ExportSwift(progress: .silent)
+ let url = Self.inputsDirectory.appendingPathComponent(input)
+ let sourceFile = Parser.parse(source: try String(contentsOf: url, encoding: .utf8))
+ try swiftAPI.addSourceFile(sourceFile, input)
+ let name = url.deletingPathExtension().lastPathComponent
+ try snapshot(swiftAPI: swiftAPI, name: name)
+ }
+}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/ImportTSTests.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/ImportTSTests.swift
new file mode 100644
index 00000000..71b0e005
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/ImportTSTests.swift
@@ -0,0 +1,32 @@
+import Testing
+import Foundation
+@testable import BridgeJSTool
+
+@Suite struct ImportTSTests {
+ static let inputsDirectory = URL(fileURLWithPath: #filePath).deletingLastPathComponent().appendingPathComponent(
+ "Inputs"
+ )
+
+ static func collectInputs() -> [String] {
+ let fileManager = FileManager.default
+ let inputs = try! fileManager.contentsOfDirectory(atPath: Self.inputsDirectory.path)
+ return inputs.filter { $0.hasSuffix(".d.ts") }
+ }
+
+ @Test(arguments: collectInputs())
+ func snapshot(input: String) throws {
+ var api = ImportTS(progress: .silent, moduleName: "Check")
+ let url = Self.inputsDirectory.appendingPathComponent(input)
+ let tsconfigPath = url.deletingLastPathComponent().appendingPathComponent("tsconfig.json")
+ try api.addSourceFile(url.path, tsconfigPath: tsconfigPath.path)
+ let outputSwift = try #require(try api.finalize())
+ let name = url.deletingPathExtension().deletingPathExtension().deletingPathExtension().lastPathComponent
+ try assertSnapshot(
+ name: name,
+ filePath: #filePath,
+ function: #function,
+ input: outputSwift.data(using: .utf8)!,
+ fileExtension: "swift"
+ )
+ }
+}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/ArrayParameter.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/ArrayParameter.d.ts
new file mode 100644
index 00000000..59674e07
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/ArrayParameter.d.ts
@@ -0,0 +1,3 @@
+export function checkArray(a: number[]): void;
+export function checkArrayWithLength(a: number[], b: number): void;
+export function checkArray(a: Array): void;
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Interface.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Interface.d.ts
new file mode 100644
index 00000000..14a8bfad
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/Interface.d.ts
@@ -0,0 +1,6 @@
+interface Animatable {
+ animate(keyframes: any, options: any): any;
+ getAnimations(options: any): any;
+}
+
+export function returnAnimatable(): Animatable;
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveParameters.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveParameters.d.ts
new file mode 100644
index 00000000..81a36c53
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveParameters.d.ts
@@ -0,0 +1 @@
+export function check(a: number, b: boolean): void;
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveParameters.swift
new file mode 100644
index 00000000..62e78008
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveParameters.swift
@@ -0,0 +1 @@
+@JS func check(a: Int, b: Float, c: Double, d: Bool) {}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveReturn.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveReturn.d.ts
new file mode 100644
index 00000000..ba22fef1
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveReturn.d.ts
@@ -0,0 +1,2 @@
+export function checkNumber(): number;
+export function checkBoolean(): boolean;
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveReturn.swift
new file mode 100644
index 00000000..96a5dbc3
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/PrimitiveReturn.swift
@@ -0,0 +1,4 @@
+@JS func checkInt() -> Int { fatalError() }
+@JS func checkFloat() -> Float { fatalError() }
+@JS func checkDouble() -> Double { fatalError() }
+@JS func checkBool() -> Bool { fatalError() }
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.d.ts
new file mode 100644
index 00000000..c252c9bb
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.d.ts
@@ -0,0 +1,2 @@
+export function checkString(a: string): void;
+export function checkStringWithLength(a: string, b: number): void;
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.swift
new file mode 100644
index 00000000..e6763d4c
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringParameter.swift
@@ -0,0 +1 @@
+@JS func checkString(a: String) {}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringReturn.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringReturn.d.ts
new file mode 100644
index 00000000..0be0ecd5
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringReturn.d.ts
@@ -0,0 +1 @@
+export function checkString(): string;
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringReturn.swift
new file mode 100644
index 00000000..fe070f0d
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/StringReturn.swift
@@ -0,0 +1 @@
+@JS func checkString() -> String { fatalError() }
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift
new file mode 100644
index 00000000..a803504f
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/SwiftClass.swift
@@ -0,0 +1,17 @@
+@JS class Greeter {
+ var name: String
+
+ @JS init(name: String) {
+ self.name = name
+ }
+ @JS func greet() -> String {
+ return "Hello, " + self.name + "!"
+ }
+ @JS func changeName(name: String) {
+ self.name = name
+ }
+}
+
+@JS func takeGreeter(greeter: Greeter) {
+ print(greeter.greet())
+}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/TypeAlias.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/TypeAlias.d.ts
new file mode 100644
index 00000000..6c74bd3c
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/TypeAlias.d.ts
@@ -0,0 +1,3 @@
+export type MyType = number;
+
+export function checkSimple(a: MyType): void;
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/TypeScriptClass.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/TypeScriptClass.d.ts
new file mode 100644
index 00000000..d10c0138
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/TypeScriptClass.d.ts
@@ -0,0 +1,5 @@
+export class Greeter {
+ constructor(name: string);
+ greet(): string;
+ changeName(name: string): void;
+}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/VoidParameterVoidReturn.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/VoidParameterVoidReturn.d.ts
new file mode 100644
index 00000000..048ef753
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/VoidParameterVoidReturn.d.ts
@@ -0,0 +1 @@
+export function check(): void;
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/VoidParameterVoidReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/VoidParameterVoidReturn.swift
new file mode 100644
index 00000000..ba0cf5d2
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/Inputs/VoidParameterVoidReturn.swift
@@ -0,0 +1 @@
+@JS func check() {}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/SnapshotTesting.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/SnapshotTesting.swift
new file mode 100644
index 00000000..28b34bf6
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/SnapshotTesting.swift
@@ -0,0 +1,42 @@
+import Testing
+import Foundation
+
+func assertSnapshot(
+ name: String? = nil,
+ filePath: String = #filePath,
+ function: String = #function,
+ sourceLocation: SourceLocation = #_sourceLocation,
+ variant: String? = nil,
+ input: Data,
+ fileExtension: String = "json"
+) throws {
+ let testFileName = URL(fileURLWithPath: filePath).deletingPathExtension().lastPathComponent
+ let snapshotDir = URL(fileURLWithPath: filePath)
+ .deletingLastPathComponent()
+ .appendingPathComponent("__Snapshots__")
+ .appendingPathComponent(testFileName)
+ try FileManager.default.createDirectory(at: snapshotDir, withIntermediateDirectories: true)
+ let snapshotName = name ?? String(function[.. Comment {
+ "Snapshot mismatch: \(actualFilePath) \(snapshotPath.path)"
+ }
+ if !ok {
+ try input.write(to: URL(fileURLWithPath: actualFilePath))
+ }
+ if ProcessInfo.processInfo.environment["UPDATE_SNAPSHOTS"] == nil {
+ #expect(ok, buildComment(), sourceLocation: sourceLocation)
+ } else {
+ try input.write(to: snapshotPath)
+ }
+ } else {
+ try input.write(to: snapshotPath)
+ #expect(Bool(false), "Snapshot created at \(snapshotPath.path)", sourceLocation: sourceLocation)
+ }
+}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/TemporaryDirectory.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/TemporaryDirectory.swift
new file mode 100644
index 00000000..199380fa
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/TemporaryDirectory.swift
@@ -0,0 +1,27 @@
+import Foundation
+
+struct MakeTemporaryDirectoryError: Error {
+ let error: CInt
+}
+
+internal func withTemporaryDirectory(body: (URL, _ retain: inout Bool) throws -> T) throws -> T {
+ // Create a temporary directory using mkdtemp
+ var template = FileManager.default.temporaryDirectory.appendingPathComponent("PackageToJSTests.XXXXXX").path
+ return try template.withUTF8 { template in
+ let copy = UnsafeMutableBufferPointer.allocate(capacity: template.count + 1)
+ template.copyBytes(to: copy)
+ copy[template.count] = 0
+
+ guard let result = mkdtemp(copy.baseAddress!) else {
+ throw MakeTemporaryDirectoryError(error: errno)
+ }
+ let tempDir = URL(fileURLWithPath: String(cString: result))
+ var retain = false
+ defer {
+ if !retain {
+ try? FileManager.default.removeItem(at: tempDir)
+ }
+ }
+ return try body(tempDir, &retain)
+ }
+}
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.d.ts
new file mode 100644
index 00000000..2a6771ca
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.d.ts
@@ -0,0 +1,20 @@
+// 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 type Exports = {
+}
+export type Imports = {
+ checkArray(a: any): void;
+ checkArrayWithLength(a: any, b: number): void;
+ checkArray(a: any): void;
+}
+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/ArrayParameter.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js
new file mode 100644
index 00000000..caad458d
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/ArrayParameter.Import.js
@@ -0,0 +1,62 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_checkArray"] = function bjs_checkArray(a) {
+ options.imports.checkArray(swift.memory.getObject(a));
+ }
+ TestModule["bjs_checkArrayWithLength"] = function bjs_checkArrayWithLength(a, b) {
+ options.imports.checkArrayWithLength(swift.memory.getObject(a), b);
+ }
+ TestModule["bjs_checkArray"] = function bjs_checkArray(a) {
+ options.imports.checkArray(swift.memory.getObject(a));
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.d.ts
new file mode 100644
index 00000000..1e7ca6ab
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+}
+export type Imports = {
+ returnAnimatable(): any;
+}
+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/Interface.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js
new file mode 100644
index 00000000..4b381185
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/Interface.Import.js
@@ -0,0 +1,65 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_returnAnimatable"] = function bjs_returnAnimatable() {
+ let ret = options.imports.returnAnimatable();
+ return swift.memory.retain(ret);
+ }
+ TestModule["bjs_Animatable_animate"] = function bjs_Animatable_animate(self, keyframes, options) {
+ let ret = swift.memory.getObject(self).animate(swift.memory.getObject(keyframes), swift.memory.getObject(options));
+ return swift.memory.retain(ret);
+ }
+ TestModule["bjs_Animatable_getAnimations"] = function bjs_Animatable_getAnimations(self, options) {
+ let ret = swift.memory.getObject(self).getAnimations(swift.memory.getObject(options));
+ return swift.memory.retain(ret);
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.d.ts
new file mode 100644
index 00000000..a9c37f37
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+ check(a: number, b: number, c: number, d: boolean): void;
+}
+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/PrimitiveParameters.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js
new file mode 100644
index 00000000..2d9ee4b1
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Export.js
@@ -0,0 +1,55 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+ check: function bjs_check(a, b, c, d) {
+ instance.exports.bjs_check(a, b, c, d);
+ },
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.d.ts
new file mode 100644
index 00000000..5442ebfa
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+}
+export type Imports = {
+ check(a: number, b: boolean): void;
+}
+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/PrimitiveParameters.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js
new file mode 100644
index 00000000..0d871bbb
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveParameters.Import.js
@@ -0,0 +1,56 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_check"] = function bjs_check(a, b) {
+ options.imports.check(a, b);
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.d.ts
new file mode 100644
index 00000000..da7f5977
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.d.ts
@@ -0,0 +1,21 @@
+// 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 type Exports = {
+ checkInt(): number;
+ checkFloat(): number;
+ checkDouble(): number;
+ checkBool(): boolean;
+}
+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/PrimitiveReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js
new file mode 100644
index 00000000..8a66f041
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Export.js
@@ -0,0 +1,68 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+ checkInt: function bjs_checkInt() {
+ const ret = instance.exports.bjs_checkInt();
+ return ret;
+ },
+ checkFloat: function bjs_checkFloat() {
+ const ret = instance.exports.bjs_checkFloat();
+ return ret;
+ },
+ checkDouble: function bjs_checkDouble() {
+ const ret = instance.exports.bjs_checkDouble();
+ return ret;
+ },
+ checkBool: function bjs_checkBool() {
+ const ret = instance.exports.bjs_checkBool() !== 0;
+ return ret;
+ },
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.d.ts
new file mode 100644
index 00000000..ad63bd7d
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.d.ts
@@ -0,0 +1,19 @@
+// 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 type Exports = {
+}
+export type Imports = {
+ checkNumber(): number;
+ checkBoolean(): boolean;
+}
+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/PrimitiveReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js
new file mode 100644
index 00000000..a638f864
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/PrimitiveReturn.Import.js
@@ -0,0 +1,61 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_checkNumber"] = function bjs_checkNumber() {
+ let ret = options.imports.checkNumber();
+ return ret;
+ }
+ TestModule["bjs_checkBoolean"] = function bjs_checkBoolean() {
+ let ret = options.imports.checkBoolean();
+ return ret !== 0;
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.d.ts
new file mode 100644
index 00000000..a83fca6f
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+ checkString(a: string): void;
+}
+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/StringParameter.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js
new file mode 100644
index 00000000..c13cd358
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Export.js
@@ -0,0 +1,58 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+ checkString: function bjs_checkString(a) {
+ const aBytes = textEncoder.encode(a);
+ const aId = swift.memory.retain(aBytes);
+ instance.exports.bjs_checkString(aId, aBytes.length);
+ swift.memory.release(aId);
+ },
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.d.ts
new file mode 100644
index 00000000..09fd7b63
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.d.ts
@@ -0,0 +1,19 @@
+// 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 type Exports = {
+}
+export type Imports = {
+ checkString(a: string): void;
+ checkStringWithLength(a: string, b: number): void;
+}
+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/StringParameter.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js
new file mode 100644
index 00000000..6e5d4bdc
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringParameter.Import.js
@@ -0,0 +1,63 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_checkString"] = function bjs_checkString(a) {
+ const aObject = swift.memory.getObject(a);
+ swift.memory.release(a);
+ options.imports.checkString(aObject);
+ }
+ TestModule["bjs_checkStringWithLength"] = function bjs_checkStringWithLength(a, b) {
+ const aObject = swift.memory.getObject(a);
+ swift.memory.release(a);
+ options.imports.checkStringWithLength(aObject, b);
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.d.ts
new file mode 100644
index 00000000..c6a9f65a
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+ checkString(): string;
+}
+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/StringReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js
new file mode 100644
index 00000000..0208d8ce
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Export.js
@@ -0,0 +1,58 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+ checkString: function bjs_checkString() {
+ instance.exports.bjs_checkString();
+ const ret = tmpRetString;
+ tmpRetString = undefined;
+ return ret;
+ },
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.d.ts
new file mode 100644
index 00000000..cb778366
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+}
+export type Imports = {
+ checkString(): string;
+}
+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/StringReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js
new file mode 100644
index 00000000..26e57959
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/StringReturn.Import.js
@@ -0,0 +1,58 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_checkString"] = function bjs_checkString() {
+ let ret = options.imports.checkString();
+ tmpRetBytes = textEncoder.encode(ret);
+ return tmpRetBytes.length;
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts
new file mode 100644
index 00000000..fd376d57
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.d.ts
@@ -0,0 +1,32 @@
+// 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`.
+
+/// 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 Greeter extends SwiftHeapObject {
+ greet(): string;
+ changeName(name: string): void;
+}
+export type Exports = {
+ Greeter: {
+ new(name: string): Greeter;
+ }
+ takeGreeter(greeter: Greeter): void;
+}
+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/SwiftClass.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js
new file mode 100644
index 00000000..971b9d69
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/SwiftClass.Export.js
@@ -0,0 +1,92 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @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 {
+ constructor(pointer, deinit) {
+ this.pointer = pointer;
+ this.hasReleased = false;
+ this.deinit = deinit;
+ this.registry = new FinalizationRegistry((pointer) => {
+ deinit(pointer);
+ });
+ this.registry.register(this, this.pointer);
+ }
+
+ release() {
+ this.registry.unregister(this);
+ this.deinit(this.pointer);
+ }
+ }
+ class Greeter extends SwiftHeapObject {
+ constructor(name) {
+ const nameBytes = textEncoder.encode(name);
+ const nameId = swift.memory.retain(nameBytes);
+ super(instance.exports.bjs_Greeter_init(nameId, nameBytes.length), instance.exports.bjs_Greeter_deinit);
+ swift.memory.release(nameId);
+ }
+ greet() {
+ instance.exports.bjs_Greeter_greet(this.pointer);
+ const ret = tmpRetString;
+ tmpRetString = undefined;
+ return ret;
+ }
+ changeName(name) {
+ const nameBytes = textEncoder.encode(name);
+ const nameId = swift.memory.retain(nameBytes);
+ instance.exports.bjs_Greeter_changeName(this.pointer, nameId, nameBytes.length);
+ swift.memory.release(nameId);
+ }
+ }
+ return {
+ Greeter,
+ takeGreeter: function bjs_takeGreeter(greeter) {
+ instance.exports.bjs_takeGreeter(greeter.pointer);
+ },
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.d.ts
new file mode 100644
index 00000000..da5dfb07
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+}
+export type Imports = {
+ checkSimple(a: number): void;
+}
+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/TypeAlias.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js
new file mode 100644
index 00000000..e5909f6c
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeAlias.Import.js
@@ -0,0 +1,56 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_checkSimple"] = function bjs_checkSimple(a) {
+ options.imports.checkSimple(a);
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.d.ts
new file mode 100644
index 00000000..818d57a9
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.d.ts
@@ -0,0 +1,17 @@
+// 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 type Exports = {
+}
+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/TypeScriptClass.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js
new file mode 100644
index 00000000..c7ae6a22
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/TypeScriptClass.Import.js
@@ -0,0 +1,63 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_Greeter_greet"] = function bjs_Greeter_greet(self) {
+ let ret = swift.memory.getObject(self).greet();
+ tmpRetBytes = textEncoder.encode(ret);
+ return tmpRetBytes.length;
+ }
+ TestModule["bjs_Greeter_changeName"] = function bjs_Greeter_changeName(self, name) {
+ const nameObject = swift.memory.getObject(name);
+ swift.memory.release(name);
+ swift.memory.getObject(self).changeName(nameObject);
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.d.ts
new file mode 100644
index 00000000..be85a00f
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+ check(): void;
+}
+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/VoidParameterVoidReturn.Export.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js
new file mode 100644
index 00000000..a3dae190
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Export.js
@@ -0,0 +1,55 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+ check: function bjs_check() {
+ instance.exports.bjs_check();
+ },
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.d.ts b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.d.ts
new file mode 100644
index 00000000..8cd1e806
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.d.ts
@@ -0,0 +1,18 @@
+// 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 type Exports = {
+}
+export type Imports = {
+ check(): void;
+}
+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/VoidParameterVoidReturn.Import.js b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js
new file mode 100644
index 00000000..db9312aa
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/BridgeJSLinkTests/VoidParameterVoidReturn.Import.js
@@ -0,0 +1,56 @@
+// 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 async function createInstantiator(options, swift) {
+ let instance;
+ let memory;
+ const textDecoder = new TextDecoder("utf-8");
+ const textEncoder = new TextEncoder("utf-8");
+
+ let tmpRetString;
+ let tmpRetBytes;
+ return {
+ /** @param {WebAssembly.Imports} importObject */
+ addImports: (importObject) => {
+ const bjs = {};
+ importObject["bjs"] = bjs;
+ bjs["return_string"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ tmpRetString = textDecoder.decode(bytes);
+ }
+ bjs["init_memory"] = function(sourceId, bytesPtr) {
+ const source = swift.memory.getObject(sourceId);
+ const bytes = new Uint8Array(memory.buffer, bytesPtr);
+ bytes.set(source);
+ }
+ bjs["make_jsstring"] = function(ptr, len) {
+ const bytes = new Uint8Array(memory.buffer, ptr, len);
+ return swift.memory.retain(textDecoder.decode(bytes));
+ }
+ bjs["init_memory_with_result"] = function(ptr, len) {
+ const target = new Uint8Array(memory.buffer, ptr, len);
+ target.set(tmpRetBytes);
+ tmpRetBytes = undefined;
+ }
+ const TestModule = importObject["TestModule"] = {};
+ TestModule["bjs_check"] = function bjs_check() {
+ options.imports.check();
+ }
+ },
+ setInstance: (i) => {
+ instance = i;
+ memory = instance.exports.memory;
+ },
+ /** @param {WebAssembly.Instance} instance */
+ createExports: (instance) => {
+ const js = swift.memory.heap;
+
+ return {
+
+ };
+ },
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.json
new file mode 100644
index 00000000..4b2dafa1
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.json
@@ -0,0 +1,54 @@
+{
+ "classes" : [
+
+ ],
+ "functions" : [
+ {
+ "abiName" : "bjs_check",
+ "name" : "check",
+ "parameters" : [
+ {
+ "label" : "a",
+ "name" : "a",
+ "type" : {
+ "int" : {
+
+ }
+ }
+ },
+ {
+ "label" : "b",
+ "name" : "b",
+ "type" : {
+ "float" : {
+
+ }
+ }
+ },
+ {
+ "label" : "c",
+ "name" : "c",
+ "type" : {
+ "double" : {
+
+ }
+ }
+ },
+ {
+ "label" : "d",
+ "name" : "d",
+ "type" : {
+ "bool" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "void" : {
+
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.swift
new file mode 100644
index 00000000..6df14156
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveParameters.swift
@@ -0,0 +1,15 @@
+// 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`.
+@_extern(wasm, module: "bjs", name: "return_string")
+private func _return_string(_ ptr: UnsafePointer?, _ len: Int32)
+@_extern(wasm, module: "bjs", name: "init_memory")
+private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?)
+
+@_expose(wasm, "bjs_check")
+@_cdecl("bjs_check")
+public func _bjs_check(a: Int32, b: Float32, c: Float64, d: Int32) -> Void {
+ check(a: Int(a), b: b, c: c, d: d == 1)
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.json
new file mode 100644
index 00000000..ae672cb5
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.json
@@ -0,0 +1,55 @@
+{
+ "classes" : [
+
+ ],
+ "functions" : [
+ {
+ "abiName" : "bjs_checkInt",
+ "name" : "checkInt",
+ "parameters" : [
+
+ ],
+ "returnType" : {
+ "int" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_checkFloat",
+ "name" : "checkFloat",
+ "parameters" : [
+
+ ],
+ "returnType" : {
+ "float" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_checkDouble",
+ "name" : "checkDouble",
+ "parameters" : [
+
+ ],
+ "returnType" : {
+ "double" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_checkBool",
+ "name" : "checkBool",
+ "parameters" : [
+
+ ],
+ "returnType" : {
+ "bool" : {
+
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.swift
new file mode 100644
index 00000000..a24b2b31
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/PrimitiveReturn.swift
@@ -0,0 +1,37 @@
+// 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`.
+@_extern(wasm, module: "bjs", name: "return_string")
+private func _return_string(_ ptr: UnsafePointer?, _ len: Int32)
+@_extern(wasm, module: "bjs", name: "init_memory")
+private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?)
+
+@_expose(wasm, "bjs_checkInt")
+@_cdecl("bjs_checkInt")
+public func _bjs_checkInt() -> Int32 {
+ let ret = checkInt()
+ return Int32(ret)
+}
+
+@_expose(wasm, "bjs_checkFloat")
+@_cdecl("bjs_checkFloat")
+public func _bjs_checkFloat() -> Float32 {
+ let ret = checkFloat()
+ return Float32(ret)
+}
+
+@_expose(wasm, "bjs_checkDouble")
+@_cdecl("bjs_checkDouble")
+public func _bjs_checkDouble() -> Float64 {
+ let ret = checkDouble()
+ return Float64(ret)
+}
+
+@_expose(wasm, "bjs_checkBool")
+@_cdecl("bjs_checkBool")
+public func _bjs_checkBool() -> Int32 {
+ let ret = checkBool()
+ return Int32(ret ? 1 : 0)
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json
new file mode 100644
index 00000000..0fea9735
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.json
@@ -0,0 +1,27 @@
+{
+ "classes" : [
+
+ ],
+ "functions" : [
+ {
+ "abiName" : "bjs_checkString",
+ "name" : "checkString",
+ "parameters" : [
+ {
+ "label" : "a",
+ "name" : "a",
+ "type" : {
+ "string" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "void" : {
+
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.swift
new file mode 100644
index 00000000..080f028e
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringParameter.swift
@@ -0,0 +1,19 @@
+// 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`.
+@_extern(wasm, module: "bjs", name: "return_string")
+private func _return_string(_ ptr: UnsafePointer?, _ len: Int32)
+@_extern(wasm, module: "bjs", name: "init_memory")
+private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?)
+
+@_expose(wasm, "bjs_checkString")
+@_cdecl("bjs_checkString")
+public func _bjs_checkString(aBytes: Int32, aLen: Int32) -> Void {
+ let a = String(unsafeUninitializedCapacity: Int(aLen)) { b in
+ _init_memory(aBytes, b.baseAddress.unsafelyUnwrapped)
+ return Int(aLen)
+ }
+ checkString(a: a)
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.json
new file mode 100644
index 00000000..c773d0d2
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.json
@@ -0,0 +1,19 @@
+{
+ "classes" : [
+
+ ],
+ "functions" : [
+ {
+ "abiName" : "bjs_checkString",
+ "name" : "checkString",
+ "parameters" : [
+
+ ],
+ "returnType" : {
+ "string" : {
+
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.swift
new file mode 100644
index 00000000..bf0be042
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/StringReturn.swift
@@ -0,0 +1,18 @@
+// 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`.
+@_extern(wasm, module: "bjs", name: "return_string")
+private func _return_string(_ ptr: UnsafePointer?, _ len: Int32)
+@_extern(wasm, module: "bjs", name: "init_memory")
+private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?)
+
+@_expose(wasm, "bjs_checkString")
+@_cdecl("bjs_checkString")
+public func _bjs_checkString() -> Void {
+ var ret = checkString()
+ return ret.withUTF8 { ptr in
+ _return_string(ptr.baseAddress, Int32(ptr.count))
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json
new file mode 100644
index 00000000..2aff4c93
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.json
@@ -0,0 +1,77 @@
+{
+ "classes" : [
+ {
+ "constructor" : {
+ "abiName" : "bjs_Greeter_init",
+ "parameters" : [
+ {
+ "label" : "name",
+ "name" : "name",
+ "type" : {
+ "string" : {
+
+ }
+ }
+ }
+ ]
+ },
+ "methods" : [
+ {
+ "abiName" : "bjs_Greeter_greet",
+ "name" : "greet",
+ "parameters" : [
+
+ ],
+ "returnType" : {
+ "string" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_Greeter_changeName",
+ "name" : "changeName",
+ "parameters" : [
+ {
+ "label" : "name",
+ "name" : "name",
+ "type" : {
+ "string" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "void" : {
+
+ }
+ }
+ }
+ ],
+ "name" : "Greeter"
+ }
+ ],
+ "functions" : [
+ {
+ "abiName" : "bjs_takeGreeter",
+ "name" : "takeGreeter",
+ "parameters" : [
+ {
+ "label" : "greeter",
+ "name" : "greeter",
+ "type" : {
+ "swiftHeapObject" : {
+ "_0" : "Greeter"
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "void" : {
+
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.swift
new file mode 100644
index 00000000..20fd9c94
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/SwiftClass.swift
@@ -0,0 +1,51 @@
+// 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`.
+@_extern(wasm, module: "bjs", name: "return_string")
+private func _return_string(_ ptr: UnsafePointer?, _ len: Int32)
+@_extern(wasm, module: "bjs", name: "init_memory")
+private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?)
+
+@_expose(wasm, "bjs_takeGreeter")
+@_cdecl("bjs_takeGreeter")
+public func _bjs_takeGreeter(greeter: UnsafeMutableRawPointer) -> Void {
+ takeGreeter(greeter: Unmanaged.fromOpaque(greeter).takeUnretainedValue())
+}
+
+@_expose(wasm, "bjs_Greeter_init")
+@_cdecl("bjs_Greeter_init")
+public func _bjs_Greeter_init(nameBytes: Int32, nameLen: Int32) -> UnsafeMutableRawPointer {
+ let name = String(unsafeUninitializedCapacity: Int(nameLen)) { b in
+ _init_memory(nameBytes, b.baseAddress.unsafelyUnwrapped)
+ return Int(nameLen)
+ }
+ let ret = Greeter(name: name)
+ return Unmanaged.passRetained(ret).toOpaque()
+}
+
+@_expose(wasm, "bjs_Greeter_greet")
+@_cdecl("bjs_Greeter_greet")
+public func _bjs_Greeter_greet(_self: UnsafeMutableRawPointer) -> Void {
+ var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().greet()
+ return ret.withUTF8 { ptr in
+ _return_string(ptr.baseAddress, Int32(ptr.count))
+ }
+}
+
+@_expose(wasm, "bjs_Greeter_changeName")
+@_cdecl("bjs_Greeter_changeName")
+public func _bjs_Greeter_changeName(_self: UnsafeMutableRawPointer, nameBytes: Int32, nameLen: Int32) -> Void {
+ let name = String(unsafeUninitializedCapacity: Int(nameLen)) { b in
+ _init_memory(nameBytes, b.baseAddress.unsafelyUnwrapped)
+ return Int(nameLen)
+ }
+ Unmanaged.fromOpaque(_self).takeUnretainedValue().changeName(name: name)
+}
+
+@_expose(wasm, "bjs_Greeter_deinit")
+@_cdecl("bjs_Greeter_deinit")
+public func _bjs_Greeter_deinit(pointer: UnsafeMutableRawPointer) {
+ Unmanaged.fromOpaque(pointer).release()
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.json b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.json
new file mode 100644
index 00000000..f82cdb82
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.json
@@ -0,0 +1,19 @@
+{
+ "classes" : [
+
+ ],
+ "functions" : [
+ {
+ "abiName" : "bjs_check",
+ "name" : "check",
+ "parameters" : [
+
+ ],
+ "returnType" : {
+ "void" : {
+
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.swift
new file mode 100644
index 00000000..cf4b76fe
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ExportSwiftTests/VoidParameterVoidReturn.swift
@@ -0,0 +1,15 @@
+// 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`.
+@_extern(wasm, module: "bjs", name: "return_string")
+private func _return_string(_ ptr: UnsafePointer?, _ len: Int32)
+@_extern(wasm, module: "bjs", name: "init_memory")
+private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?)
+
+@_expose(wasm, "bjs_check")
+@_cdecl("bjs_check")
+public func _bjs_check() -> Void {
+ check()
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift
new file mode 100644
index 00000000..1773223b
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/ArrayParameter.swift
@@ -0,0 +1,34 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+func checkArray(_ a: JSObject) -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_checkArray")
+ func bjs_checkArray(_ a: Int32) -> Void
+ bjs_checkArray(Int32(bitPattern: a.id))
+}
+
+func checkArrayWithLength(_ a: JSObject, _ b: Double) -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_checkArrayWithLength")
+ func bjs_checkArrayWithLength(_ a: Int32, _ b: Float64) -> Void
+ bjs_checkArrayWithLength(Int32(bitPattern: a.id), b)
+}
+
+func checkArray(_ a: JSObject) -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_checkArray")
+ func bjs_checkArray(_ a: Int32) -> Void
+ bjs_checkArray(Int32(bitPattern: a.id))
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift
new file mode 100644
index 00000000..c565a2f8
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/Interface.swift
@@ -0,0 +1,50 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+func returnAnimatable() -> Animatable {
+ @_extern(wasm, module: "Check", name: "bjs_returnAnimatable")
+ func bjs_returnAnimatable() -> Int32
+ let ret = bjs_returnAnimatable()
+ return Animatable(takingThis: ret)
+}
+
+struct Animatable {
+ let this: JSObject
+
+ init(this: JSObject) {
+ self.this = this
+ }
+
+ init(takingThis this: Int32) {
+ self.this = JSObject(id: UInt32(bitPattern: this))
+ }
+
+ func animate(_ keyframes: JSObject, _ options: JSObject) -> JSObject {
+ @_extern(wasm, module: "Check", name: "bjs_Animatable_animate")
+ func bjs_Animatable_animate(_ self: Int32, _ keyframes: Int32, _ options: Int32) -> Int32
+ let ret = bjs_Animatable_animate(Int32(bitPattern: self.this.id), Int32(bitPattern: keyframes.id), Int32(bitPattern: options.id))
+ return JSObject(id: UInt32(bitPattern: ret))
+ }
+
+ func getAnimations(_ options: JSObject) -> JSObject {
+ @_extern(wasm, module: "Check", name: "bjs_Animatable_getAnimations")
+ func bjs_Animatable_getAnimations(_ self: Int32, _ options: Int32) -> Int32
+ let ret = bjs_Animatable_getAnimations(Int32(bitPattern: self.this.id), Int32(bitPattern: options.id))
+ return JSObject(id: UInt32(bitPattern: ret))
+ }
+
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift
new file mode 100644
index 00000000..4ab7f754
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveParameters.swift
@@ -0,0 +1,22 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+func check(_ a: Double, _ b: Bool) -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_check")
+ func bjs_check(_ a: Float64, _ b: Int32) -> Void
+ bjs_check(a, Int32(b ? 1 : 0))
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift
new file mode 100644
index 00000000..a60c9323
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/PrimitiveReturn.swift
@@ -0,0 +1,30 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+func checkNumber() -> Double {
+ @_extern(wasm, module: "Check", name: "bjs_checkNumber")
+ func bjs_checkNumber() -> Float64
+ let ret = bjs_checkNumber()
+ return Double(ret)
+}
+
+func checkBoolean() -> Bool {
+ @_extern(wasm, module: "Check", name: "bjs_checkBoolean")
+ func bjs_checkBoolean() -> Int32
+ let ret = bjs_checkBoolean()
+ return ret == 1
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift
new file mode 100644
index 00000000..491978bc
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringParameter.swift
@@ -0,0 +1,36 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+func checkString(_ a: String) -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_checkString")
+ func bjs_checkString(_ a: Int32) -> Void
+ var a = a
+ let aId = a.withUTF8 { b in
+ _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count))
+ }
+ bjs_checkString(aId)
+}
+
+func checkStringWithLength(_ a: String, _ b: Double) -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_checkStringWithLength")
+ func bjs_checkStringWithLength(_ a: Int32, _ b: Float64) -> Void
+ var a = a
+ let aId = a.withUTF8 { b in
+ _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count))
+ }
+ bjs_checkStringWithLength(aId, b)
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift
new file mode 100644
index 00000000..ce32a643
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/StringReturn.swift
@@ -0,0 +1,26 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+func checkString() -> String {
+ @_extern(wasm, module: "Check", name: "bjs_checkString")
+ func bjs_checkString() -> Int32
+ let ret = bjs_checkString()
+ return String(unsafeUninitializedCapacity: Int(ret)) { b in
+ _init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret))
+ return Int(ret)
+ }
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift
new file mode 100644
index 00000000..79f29c92
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeAlias.swift
@@ -0,0 +1,22 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+func checkSimple(_ a: Double) -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_checkSimple")
+ func bjs_checkSimple(_ a: Float64) -> Void
+ bjs_checkSimple(a)
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift
new file mode 100644
index 00000000..993a1417
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/TypeScriptClass.swift
@@ -0,0 +1,60 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+struct Greeter {
+ let this: JSObject
+
+ init(this: JSObject) {
+ self.this = this
+ }
+
+ init(takingThis this: Int32) {
+ self.this = JSObject(id: UInt32(bitPattern: this))
+ }
+
+ init(_ name: String) {
+ @_extern(wasm, module: "Check", name: "bjs_Greeter_init")
+ func bjs_Greeter_init(_ name: Int32) -> Int32
+ var name = name
+ let nameId = name.withUTF8 { b in
+ _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count))
+ }
+ let ret = bjs_Greeter_init(nameId)
+ self.this = ret
+ }
+
+ func greet() -> String {
+ @_extern(wasm, module: "Check", name: "bjs_Greeter_greet")
+ func bjs_Greeter_greet(_ self: Int32) -> Int32
+ let ret = bjs_Greeter_greet(Int32(bitPattern: self.this.id))
+ return String(unsafeUninitializedCapacity: Int(ret)) { b in
+ _init_memory_with_result(b.baseAddress.unsafelyUnwrapped, Int32(ret))
+ return Int(ret)
+ }
+ }
+
+ func changeName(_ name: String) -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_Greeter_changeName")
+ func bjs_Greeter_changeName(_ self: Int32, _ name: Int32) -> Void
+ var name = name
+ let nameId = name.withUTF8 { b in
+ _make_jsstring(b.baseAddress.unsafelyUnwrapped, Int32(b.count))
+ }
+ bjs_Greeter_changeName(Int32(bitPattern: self.this.id), nameId)
+ }
+
+}
\ No newline at end of file
diff --git a/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift
new file mode 100644
index 00000000..3f2ecc78
--- /dev/null
+++ b/Plugins/BridgeJS/Tests/BridgeJSToolTests/__Snapshots__/ImportTSTests/VoidParameterVoidReturn.swift
@@ -0,0 +1,22 @@
+// 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(JSObject_id) import JavaScriptKit
+
+@_extern(wasm, module: "bjs", name: "make_jsstring")
+private func _make_jsstring(_ ptr: UnsafePointer?, _ len: Int32) -> Int32
+
+@_extern(wasm, module: "bjs", name: "init_memory_with_result")
+private func _init_memory_with_result(_ ptr: UnsafePointer?, _ len: Int32)
+
+@_extern(wasm, module: "bjs", name: "free_jsobject")
+private func _free_jsobject(_ ptr: Int32) -> Void
+
+func check() -> Void {
+ @_extern(wasm, module: "Check", name: "bjs_check")
+ func bjs_check() -> Void
+ bjs_check()
+}
\ No newline at end of file
diff --git a/Plugins/PackageToJS/Sources/BridgeJSLink b/Plugins/PackageToJS/Sources/BridgeJSLink
new file mode 120000
index 00000000..41b4d0a4
--- /dev/null
+++ b/Plugins/PackageToJS/Sources/BridgeJSLink
@@ -0,0 +1 @@
+../../BridgeJS/Sources/BridgeJSLink
\ No newline at end of file
diff --git a/Plugins/PackageToJS/Sources/PackageToJS.swift b/Plugins/PackageToJS/Sources/PackageToJS.swift
index da29164b..2b8b4458 100644
--- a/Plugins/PackageToJS/Sources/PackageToJS.swift
+++ b/Plugins/PackageToJS/Sources/PackageToJS.swift
@@ -365,6 +365,10 @@ struct PackagingPlanner {
let selfPackageDir: BuildPath
/// The path of this file itself, used to capture changes of planner code
let selfPath: BuildPath
+ /// The exported API skeletons source files
+ let exportedSkeletons: [BuildPath]
+ /// The imported API skeletons source files
+ let importedSkeletons: [BuildPath]
/// The directory for the final output
let outputDir: BuildPath
/// The directory for intermediate files
@@ -385,6 +389,8 @@ struct PackagingPlanner {
packageId: String,
intermediatesDir: BuildPath,
selfPackageDir: BuildPath,
+ exportedSkeletons: [BuildPath],
+ importedSkeletons: [BuildPath],
outputDir: BuildPath,
wasmProductArtifact: BuildPath,
wasmFilename: String,
@@ -396,6 +402,8 @@ struct PackagingPlanner {
self.options = options
self.packageId = packageId
self.selfPackageDir = selfPackageDir
+ self.exportedSkeletons = exportedSkeletons
+ self.importedSkeletons = importedSkeletons
self.outputDir = outputDir
self.intermediatesDir = intermediatesDir
self.wasmFilename = wasmFilename
@@ -555,6 +563,35 @@ struct PackagingPlanner {
)
packageInputs.append(packageJsonTask)
+ if exportedSkeletons.count > 0 || importedSkeletons.count > 0 {
+ if ProcessInfo.processInfo.environment["JAVASCRIPTKIT_EXPERIMENTAL_BRIDGEJS"] == nil {
+ fatalError(
+ "BridgeJS is still an experimental feature. Set the environment variable JAVASCRIPTKIT_EXPERIMENTAL_BRIDGEJS=1 to enable."
+ )
+ }
+ let bridgeJs = outputDir.appending(path: "bridge.js")
+ let bridgeDts = outputDir.appending(path: "bridge.d.ts")
+ packageInputs.append(
+ make.addTask(inputFiles: exportedSkeletons + importedSkeletons, output: bridgeJs) { _, scope in
+ let link = try BridgeJSLink(
+ exportedSkeletons: exportedSkeletons.map {
+ let decoder = JSONDecoder()
+ let data = try Data(contentsOf: URL(fileURLWithPath: scope.resolve(path: $0).path))
+ return try decoder.decode(ExportedSkeleton.self, from: data)
+ },
+ importedSkeletons: importedSkeletons.map {
+ let decoder = JSONDecoder()
+ let data = try Data(contentsOf: URL(fileURLWithPath: scope.resolve(path: $0).path))
+ return try decoder.decode(ImportedModuleSkeleton.self, from: data)
+ }
+ )
+ let (outputJs, outputDts) = try link.link()
+ try system.writeFile(atPath: scope.resolve(path: bridgeJs).path, content: Data(outputJs.utf8))
+ try system.writeFile(atPath: scope.resolve(path: bridgeDts).path, content: Data(outputDts.utf8))
+ }
+ )
+ }
+
// Copy the template files
for (file, output) in [
("Plugins/PackageToJS/Templates/index.js", "index.js"),
@@ -665,6 +702,8 @@ struct PackagingPlanner {
"USE_SHARED_MEMORY": triple == "wasm32-unknown-wasip1-threads",
"IS_WASI": triple.hasPrefix("wasm32-unknown-wasi"),
"USE_WASI_CDN": options.useCDN,
+ "HAS_BRIDGE": exportedSkeletons.count > 0 || importedSkeletons.count > 0,
+ "HAS_IMPORTS": importedSkeletons.count > 0,
]
let constantSubstitutions: [String: String] = [
"PACKAGE_TO_JS_MODULE_PATH": wasmFilename,
diff --git a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
index 5eb26cdf..e7f74e97 100644
--- a/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
+++ b/Plugins/PackageToJS/Sources/PackageToJSPlugin.swift
@@ -173,6 +173,8 @@ struct PackageToJSPlugin: CommandPlugin {
reportBuildFailure(build, arguments)
exit(1)
}
+ let skeletonCollector = SkeletonCollector(context: context)
+ let (exportedSkeletons, importedSkeletons) = skeletonCollector.collectFromProduct(name: productName)
let productArtifact = try build.findWasmArtifact(for: productName)
let outputDir =
if let outputPath = buildOptions.packageOptions.outputPath {
@@ -188,6 +190,8 @@ struct PackageToJSPlugin: CommandPlugin {
options: buildOptions.packageOptions,
context: context,
selfPackage: selfPackage,
+ exportedSkeletons: exportedSkeletons,
+ importedSkeletons: importedSkeletons,
outputDir: outputDir,
wasmProductArtifact: productArtifact,
wasmFilename: productArtifact.lastPathComponent
@@ -233,6 +237,9 @@ struct PackageToJSPlugin: CommandPlugin {
exit(1)
}
+ let skeletonCollector = SkeletonCollector(context: context)
+ let (exportedSkeletons, importedSkeletons) = skeletonCollector.collectFromTests()
+
// NOTE: Find the product artifact from the default build directory
// because PackageManager.BuildResult doesn't include the
// product artifact for tests.
@@ -268,6 +275,8 @@ struct PackageToJSPlugin: CommandPlugin {
options: testOptions.packageOptions,
context: context,
selfPackage: selfPackage,
+ exportedSkeletons: exportedSkeletons,
+ importedSkeletons: importedSkeletons,
outputDir: outputDir,
wasmProductArtifact: productArtifact,
// If the product artifact doesn't have a .wasm extension, add it
@@ -631,11 +640,97 @@ private func findPackageInDependencies(package: Package, id: Package.ID) -> Pack
return visit(package: package)
}
+class SkeletonCollector {
+ private var visitedProducts: Set = []
+ private var visitedTargets: Set = []
+
+ var exportedSkeletons: [URL] = []
+ var importedSkeletons: [URL] = []
+ let exportedSkeletonFile = "ExportSwift.json"
+ let importedSkeletonFile = "ImportTS.json"
+ let context: PluginContext
+
+ init(context: PluginContext) {
+ self.context = context
+ }
+
+ func collectFromProduct(name: String) -> (exportedSkeletons: [URL], importedSkeletons: [URL]) {
+ guard let product = context.package.products.first(where: { $0.name == name }) else {
+ return ([], [])
+ }
+ visit(product: product, package: context.package)
+ return (exportedSkeletons, importedSkeletons)
+ }
+
+ func collectFromTests() -> (exportedSkeletons: [URL], importedSkeletons: [URL]) {
+ let tests = context.package.targets.filter {
+ guard let target = $0 as? SwiftSourceModuleTarget else { return false }
+ return target.kind == .test
+ }
+ for test in tests {
+ visit(target: test, package: context.package)
+ }
+ return (exportedSkeletons, importedSkeletons)
+ }
+
+ private func visit(product: Product, package: Package) {
+ if visitedProducts.contains(product.id) { return }
+ visitedProducts.insert(product.id)
+ for target in product.targets {
+ visit(target: target, package: package)
+ }
+ }
+
+ private func visit(target: Target, package: Package) {
+ if visitedTargets.contains(target.id) { return }
+ visitedTargets.insert(target.id)
+ if let target = target as? SwiftSourceModuleTarget {
+ let directories = [
+ target.directoryURL.appending(path: "Generated/JavaScript"),
+ // context.pluginWorkDirectoryURL: ".build/plugins/PackageToJS/outputs/"
+ // .build/plugins/outputs/exportswift/MyApp/destination/BridgeJS/ExportSwift.json
+ context.pluginWorkDirectoryURL.deletingLastPathComponent().deletingLastPathComponent()
+ .appending(path: "outputs/\(package.id)/\(target.name)/destination/BridgeJS"),
+ ]
+ for directory in directories {
+ let exportedSkeletonURL = directory.appending(path: exportedSkeletonFile)
+ let importedSkeletonURL = directory.appending(path: importedSkeletonFile)
+ if FileManager.default.fileExists(atPath: exportedSkeletonURL.path) {
+ exportedSkeletons.append(exportedSkeletonURL)
+ }
+ if FileManager.default.fileExists(atPath: importedSkeletonURL.path) {
+ importedSkeletons.append(importedSkeletonURL)
+ }
+ }
+ }
+
+ var packageByProduct: [Product.ID: Package] = [:]
+ for packageDependency in package.dependencies {
+ for product in packageDependency.package.products {
+ packageByProduct[product.id] = packageDependency.package
+ }
+ }
+
+ for dependency in target.dependencies {
+ switch dependency {
+ case .product(let product):
+ visit(product: product, package: packageByProduct[product.id]!)
+ case .target(let target):
+ visit(target: target, package: package)
+ @unknown default:
+ continue
+ }
+ }
+ }
+}
+
extension PackagingPlanner {
init(
options: PackageToJS.PackageOptions,
context: PluginContext,
selfPackage: Package,
+ exportedSkeletons: [URL],
+ importedSkeletons: [URL],
outputDir: URL,
wasmProductArtifact: URL,
wasmFilename: String
@@ -650,6 +745,8 @@ extension PackagingPlanner {
absolute: context.pluginWorkDirectoryURL.appending(path: outputBaseName + ".tmp").path
),
selfPackageDir: BuildPath(absolute: selfPackage.directoryURL.path),
+ exportedSkeletons: exportedSkeletons.map { BuildPath(absolute: $0.path) },
+ importedSkeletons: importedSkeletons.map { BuildPath(absolute: $0.path) },
outputDir: BuildPath(absolute: outputDir.path),
wasmProductArtifact: BuildPath(absolute: wasmProductArtifact.path),
wasmFilename: wasmFilename,
diff --git a/Plugins/PackageToJS/Templates/index.d.ts b/Plugins/PackageToJS/Templates/index.d.ts
index 11d5908c..77d68efd 100644
--- a/Plugins/PackageToJS/Templates/index.d.ts
+++ b/Plugins/PackageToJS/Templates/index.d.ts
@@ -1,4 +1,4 @@
-import type { Export, ModuleSource } from './instantiate.js'
+import type { Exports, Imports, ModuleSource } from './instantiate.js'
export type Options = {
/**
@@ -7,6 +7,12 @@ export type Options = {
* If not provided, the module will be fetched from the default path.
*/
module?: ModuleSource
+/* #if HAS_IMPORTS */
+ /**
+ * The imports to use for the module
+ */
+ imports: Imports
+/* #endif */
}
/**
@@ -17,5 +23,5 @@ export type Options = {
*/
export declare function init(options?: Options): Promise<{
instance: WebAssembly.Instance,
- exports: Export
+ exports: Exports
}>
diff --git a/Plugins/PackageToJS/Templates/index.js b/Plugins/PackageToJS/Templates/index.js
index 4b8d90f6..76721511 100644
--- a/Plugins/PackageToJS/Templates/index.js
+++ b/Plugins/PackageToJS/Templates/index.js
@@ -3,13 +3,23 @@ import { instantiate } from './instantiate.js';
import { defaultBrowserSetup /* #if USE_SHARED_MEMORY */, createDefaultWorkerFactory /* #endif */} from './platforms/browser.js';
/** @type {import('./index.d').init} */
-export async function init(options = {}) {
+export async function init(_options) {
+ /** @type {import('./index.d').Options} */
+ const options = _options || {
+/* #if HAS_IMPORTS */
+ /** @returns {import('./instantiate.d').Imports} */
+ get imports() { (() => { throw new Error("No imports provided") })() }
+/* #endif */
+ };
let module = options.module;
if (!module) {
module = fetch(new URL("@PACKAGE_TO_JS_MODULE_PATH@", import.meta.url))
}
const instantiateOptions = await defaultBrowserSetup({
module,
+/* #if HAS_IMPORTS */
+ imports: options.imports,
+/* #endif */
/* #if USE_SHARED_MEMORY */
spawnWorker: createDefaultWorkerFactory()
/* #endif */
diff --git a/Plugins/PackageToJS/Templates/instantiate.d.ts b/Plugins/PackageToJS/Templates/instantiate.d.ts
index 3a88b12d..6c71d1da 100644
--- a/Plugins/PackageToJS/Templates/instantiate.d.ts
+++ b/Plugins/PackageToJS/Templates/instantiate.d.ts
@@ -1,11 +1,12 @@
import type { /* #if USE_SHARED_MEMORY */SwiftRuntimeThreadChannel, /* #endif */SwiftRuntime } from "./runtime.js";
-export type Import = {
- // TODO: Generate type from imported .d.ts files
-}
-export type Export = {
- // TODO: Generate type from .swift files
-}
+/* #if HAS_BRIDGE */
+// @ts-ignore
+export type { Imports, Exports } from "./bridge.js";
+/* #else */
+export type Imports = {}
+export type Exports = {}
+/* #endif */
/**
* The path to the WebAssembly module relative to the root of the package
@@ -59,10 +60,12 @@ export type InstantiateOptions = {
* The WebAssembly module to instantiate
*/
module: ModuleSource,
+/* #if HAS_IMPORTS */
/**
* The imports provided by the embedder
*/
- imports: Import,
+ imports: Imports,
+/* #endif */
/* #if IS_WASI */
/**
* The WASI implementation to use
@@ -86,7 +89,11 @@ export type InstantiateOptions = {
* Add imports to the WebAssembly import object
* @param imports - The imports to add
*/
- addToCoreImports?: (imports: WebAssembly.Imports) => void
+ addToCoreImports?: (
+ imports: WebAssembly.Imports,
+ getInstance: () => WebAssembly.Instance | null,
+ getExports: () => Exports | null,
+ ) => void
}
/**
@@ -95,7 +102,7 @@ export type InstantiateOptions = {
export declare function instantiate(options: InstantiateOptions): Promise<{
instance: WebAssembly.Instance,
swift: SwiftRuntime,
- exports: Export
+ exports: Exports
}>
/**
@@ -104,5 +111,5 @@ export declare function instantiate(options: InstantiateOptions): Promise<{
export declare function instantiateForThread(tid: number, startArg: number, options: InstantiateOptions): Promise<{
instance: WebAssembly.Instance,
swift: SwiftRuntime,
- exports: Export
+ exports: Exports
}>
diff --git a/Plugins/PackageToJS/Templates/instantiate.js b/Plugins/PackageToJS/Templates/instantiate.js
index a239a79c..2a41d48c 100644
--- a/Plugins/PackageToJS/Templates/instantiate.js
+++ b/Plugins/PackageToJS/Templates/instantiate.js
@@ -13,19 +13,28 @@ export const MEMORY_TYPE = {
}
/* #endif */
+/* #if HAS_BRIDGE */
+// @ts-ignore
+import { createInstantiator } from "./bridge.js"
+/* #else */
/**
* @param {import('./instantiate.d').InstantiateOptions} options
+ * @param {any} swift
*/
-async function createInstantiator(options) {
+async function createInstantiator(options, swift) {
return {
/** @param {WebAssembly.Imports} importObject */
addImports: (importObject) => {},
/** @param {WebAssembly.Instance} instance */
+ setInstance: (instance) => {},
+ /** @param {WebAssembly.Instance} instance */
createExports: (instance) => {
return {};
},
}
}
+/* #endif */
+
/** @type {import('./instantiate.d').instantiate} */
export async function instantiate(
options
@@ -58,13 +67,13 @@ async function _instantiate(
/* #if IS_WASI */
const { wasi } = options;
/* #endif */
- const instantiator = await createInstantiator(options);
const swift = new SwiftRuntime({
/* #if USE_SHARED_MEMORY */
sharedMemory: true,
threadChannel: options.threadChannel,
/* #endif */
});
+ const instantiator = await createInstantiator(options, swift);
/** @type {WebAssembly.Imports} */
const importObject = {
@@ -84,10 +93,11 @@ async function _instantiate(
/* #endif */
};
instantiator.addImports(importObject);
- options.addToCoreImports?.(importObject);
+ options.addToCoreImports?.(importObject, () => instance, () => exports);
let module;
let instance;
+ let exports;
if (moduleSource instanceof WebAssembly.Module) {
module = moduleSource;
instance = await WebAssembly.instantiate(module, importObject);
@@ -108,10 +118,12 @@ async function _instantiate(
}
swift.setInstance(instance);
+ instantiator.setInstance(instance);
+ exports = instantiator.createExports(instance);
return {
instance,
swift,
- exports: instantiator.createExports(instance),
+ exports,
}
}
diff --git a/Plugins/PackageToJS/Templates/platforms/browser.d.ts b/Plugins/PackageToJS/Templates/platforms/browser.d.ts
index a8089f8a..b851c228 100644
--- a/Plugins/PackageToJS/Templates/platforms/browser.d.ts
+++ b/Plugins/PackageToJS/Templates/platforms/browser.d.ts
@@ -1,4 +1,4 @@
-import type { InstantiateOptions, ModuleSource } from "../instantiate.js"
+import type { InstantiateOptions, ModuleSource/* #if HAS_IMPORTS */, Imports/* #endif */ } from "../instantiate.js"
export function defaultBrowserSetup(options: {
module: ModuleSource,
@@ -7,6 +7,9 @@ export function defaultBrowserSetup(options: {
onStdoutLine?: (line: string) => void,
onStderrLine?: (line: string) => void,
/* #endif */
+/* #if HAS_IMPORTS */
+ imports: Imports,
+/* #endif */
/* #if USE_SHARED_MEMORY */
spawnWorker: (module: WebAssembly.Module, memory: WebAssembly.Memory, startArg: any) => Worker,
/* #endif */
diff --git a/Plugins/PackageToJS/Templates/platforms/browser.js b/Plugins/PackageToJS/Templates/platforms/browser.js
index b1e469fb..9afd5c94 100644
--- a/Plugins/PackageToJS/Templates/platforms/browser.js
+++ b/Plugins/PackageToJS/Templates/platforms/browser.js
@@ -123,7 +123,9 @@ export async function defaultBrowserSetup(options) {
return {
module: options.module,
- imports: {},
+/* #if HAS_IMPORTS */
+ imports: options.imports,
+/* #endif */
/* #if IS_WASI */
wasi: Object.assign(wasi, {
setInstance(instance) {
diff --git a/Plugins/PackageToJS/Tests/ExampleTests.swift b/Plugins/PackageToJS/Tests/ExampleTests.swift
index c51cbfa9..7c41cf3b 100644
--- a/Plugins/PackageToJS/Tests/ExampleTests.swift
+++ b/Plugins/PackageToJS/Tests/ExampleTests.swift
@@ -73,6 +73,25 @@ extension Trait where Self == ConditionTrait {
enumerator.skipDescendants()
continue
}
+
+ // Copy symbolic links
+ if let resourceValues = try? sourcePath.resourceValues(forKeys: [.isSymbolicLinkKey]),
+ resourceValues.isSymbolicLink == true
+ {
+ try FileManager.default.createDirectory(
+ at: destinationPath.deletingLastPathComponent(),
+ withIntermediateDirectories: true,
+ attributes: nil
+ )
+ let linkDestination = try! FileManager.default.destinationOfSymbolicLink(atPath: sourcePath.path)
+ try FileManager.default.createSymbolicLink(
+ atPath: destinationPath.path,
+ withDestinationPath: linkDestination
+ )
+ enumerator.skipDescendants()
+ continue
+ }
+
// Skip directories
var isDirectory: ObjCBool = false
if FileManager.default.fileExists(atPath: sourcePath.path, isDirectory: &isDirectory) {
diff --git a/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift b/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift
index c69dcb66..03fc4c9c 100644
--- a/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift
+++ b/Plugins/PackageToJS/Tests/PackagingPlannerTests.swift
@@ -65,6 +65,8 @@ import Testing
packageId: "test",
intermediatesDir: BuildPath(prefix: "INTERMEDIATES"),
selfPackageDir: BuildPath(prefix: "SELF_PACKAGE"),
+ exportedSkeletons: [],
+ importedSkeletons: [],
outputDir: BuildPath(prefix: "OUTPUT"),
wasmProductArtifact: BuildPath(prefix: "WASM_PRODUCT_ARTIFACT"),
wasmFilename: "main.wasm",
@@ -94,6 +96,8 @@ import Testing
packageId: "test",
intermediatesDir: BuildPath(prefix: "INTERMEDIATES"),
selfPackageDir: BuildPath(prefix: "SELF_PACKAGE"),
+ exportedSkeletons: [],
+ importedSkeletons: [],
outputDir: BuildPath(prefix: "OUTPUT"),
wasmProductArtifact: BuildPath(prefix: "WASM_PRODUCT_ARTIFACT"),
wasmFilename: "main.wasm",
diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_debug.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_debug.json
index e525d134..13768da7 100644
--- a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_debug.json
+++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_debug.json
@@ -48,7 +48,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -65,7 +65,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -82,7 +82,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -99,7 +99,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -128,7 +128,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json"
],
"output" : "$OUTPUT\/package.json",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT"
]
@@ -155,7 +155,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -172,7 +172,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -189,7 +189,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.worker.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -206,7 +206,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -223,7 +223,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -240,7 +240,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -257,7 +257,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release.json
index 6e3480c5..ccfbc35c 100644
--- a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release.json
+++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release.json
@@ -62,7 +62,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -79,7 +79,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -96,7 +96,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -113,7 +113,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -143,7 +143,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json"
],
"output" : "$OUTPUT\/package.json",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT"
]
@@ -170,7 +170,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -187,7 +187,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -204,7 +204,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.worker.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -221,7 +221,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -238,7 +238,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -255,7 +255,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -272,7 +272,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_dwarf.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_dwarf.json
index e525d134..13768da7 100644
--- a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_dwarf.json
+++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_dwarf.json
@@ -48,7 +48,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -65,7 +65,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -82,7 +82,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -99,7 +99,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -128,7 +128,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json"
],
"output" : "$OUTPUT\/package.json",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT"
]
@@ -155,7 +155,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -172,7 +172,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -189,7 +189,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.worker.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -206,7 +206,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -223,7 +223,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -240,7 +240,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -257,7 +257,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_name.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_name.json
index 6e3480c5..ccfbc35c 100644
--- a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_name.json
+++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_name.json
@@ -62,7 +62,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -79,7 +79,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -96,7 +96,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -113,7 +113,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -143,7 +143,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json"
],
"output" : "$OUTPUT\/package.json",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT"
]
@@ -170,7 +170,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -187,7 +187,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -204,7 +204,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.worker.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -221,7 +221,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -238,7 +238,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -255,7 +255,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -272,7 +272,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_no_optimize.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_no_optimize.json
index e525d134..13768da7 100644
--- a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_no_optimize.json
+++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planBuild_release_no_optimize.json
@@ -48,7 +48,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -65,7 +65,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -82,7 +82,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -99,7 +99,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -128,7 +128,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json"
],
"output" : "$OUTPUT\/package.json",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT"
]
@@ -155,7 +155,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -172,7 +172,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -189,7 +189,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.worker.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -206,7 +206,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -223,7 +223,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -240,7 +240,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -257,7 +257,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
diff --git a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planTestBuild.json b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planTestBuild.json
index 2be6ce1d..89425dc8 100644
--- a/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planTestBuild.json
+++ b/Plugins/PackageToJS/Tests/__Snapshots__/PackagingPlannerTests/planTestBuild.json
@@ -73,7 +73,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/bin\/test.js"
],
"output" : "$OUTPUT\/bin\/test.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/bin"
@@ -89,7 +89,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -106,7 +106,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/index.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -123,7 +123,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -140,7 +140,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/instantiate.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -169,7 +169,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/package.json"
],
"output" : "$OUTPUT\/package.json",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT"
]
@@ -196,7 +196,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -213,7 +213,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -230,7 +230,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/browser.worker.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -247,7 +247,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -264,7 +264,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/platforms\/node.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -281,7 +281,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -298,7 +298,7 @@
"$INTERMEDIATES\/wasm-imports.json"
],
"output" : "$OUTPUT\/runtime.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/platforms",
@@ -314,7 +314,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/test.browser.html"
],
"output" : "$OUTPUT\/test.browser.html",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/bin"
@@ -329,7 +329,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/test.d.ts"
],
"output" : "$OUTPUT\/test.d.ts",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/bin"
@@ -344,7 +344,7 @@
"$SELF_PACKAGE\/Plugins\/PackageToJS\/Templates\/test.js"
],
"output" : "$OUTPUT\/test.js",
- "salt" : "eyJjb25kaXRpb25zIjp7IklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
+ "salt" : "eyJjb25kaXRpb25zIjp7IkhBU19CUklER0UiOmZhbHNlLCJIQVNfSU1QT1JUUyI6ZmFsc2UsIklTX1dBU0kiOnRydWUsIlVTRV9TSEFSRURfTUVNT1JZIjpmYWxzZSwiVVNFX1dBU0lfQ0ROIjpmYWxzZX0sInN1YnN0aXR1dGlvbnMiOnsiUEFDS0FHRV9UT19KU19NT0RVTEVfUEFUSCI6Im1haW4ud2FzbSIsIlBBQ0tBR0VfVE9fSlNfUEFDS0FHRV9OQU1FIjoidGVzdCJ9fQ==",
"wants" : [
"$OUTPUT",
"$OUTPUT\/bin"
diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/Ahead-of-Time-Code-Generation.md b/Sources/JavaScriptKit/Documentation.docc/Articles/Ahead-of-Time-Code-Generation.md
new file mode 100644
index 00000000..755f68b9
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Articles/Ahead-of-Time-Code-Generation.md
@@ -0,0 +1,169 @@
+# Ahead-of-Time Code Generation with BridgeJS
+
+Learn how to improve build times by generating BridgeJS code ahead of time.
+
+## Overview
+
+> Important: This feature is still experimental. No API stability is guaranteed, and the API may change in future releases.
+
+The BridgeJS build plugin automatically processes `@JS` annotations and TypeScript definitions during each build. While convenient, this can significantly increase build times for larger projects. To address this, JavaScriptKit provides a command plugin that lets you generate the bridge code ahead of time.
+
+## Using the Command Plugin
+
+The `swift package plugin bridge-js` command provides an alternative to the build plugin approach. By generating code once and committing it to your repository, you can:
+
+1. **Reduce build times**: Skip code generation during normal builds
+2. **Inspect generated code**: Review and version control the generated Swift code
+3. **Create reproducible builds**: Ensure consistent builds across different environments
+
+### Step 1: Configure Your Package
+
+Configure your package to use JavaScriptKit, but without including the BridgeJS build plugin:
+
+```swift
+// swift-tools-version:6.0
+
+import PackageDescription
+
+let package = Package(
+ name: "MyApp",
+ dependencies: [
+ .package(url: "https://github.com/swiftwasm/JavaScriptKit.git", branch: "main")
+ ],
+ targets: [
+ .executableTarget(
+ name: "MyApp",
+ dependencies: ["JavaScriptKit"],
+ swiftSettings: [
+ // Still required for the generated code
+ .enableExperimentalFeature("Extern")
+ ]
+ // Notice we DON'T include the BridgeJS build plugin here
+ )
+ ]
+)
+```
+
+### Step 2: Create Your Swift Code with @JS Annotations
+
+Write your Swift code with `@JS` annotations as usual:
+
+```swift
+import JavaScriptKit
+
+@JS public func calculateTotal(price: Double, quantity: Int) -> Double {
+ return price * Double(quantity)
+}
+
+@JS class Counter {
+ private var count = 0
+
+ @JS init() {}
+
+ @JS func increment() {
+ count += 1
+ }
+
+ @JS func getValue() -> Int {
+ return count
+ }
+}
+```
+
+### Step 3: Create Your TypeScript Definitions
+
+If you're importing JavaScript APIs, create your `bridge.d.ts` file as usual:
+
+```typescript
+// Sources/MyApp/bridge.d.ts
+export function consoleLog(message: string): void;
+
+export interface Document {
+ title: string;
+ getElementById(id: string): HTMLElement;
+}
+
+export function getDocument(): Document;
+```
+
+### Step 4: Generate the Bridge Code
+
+Run the command plugin to generate the bridge code:
+
+```bash
+swift package plugin bridge-js
+```
+
+This command will:
+
+1. Process all Swift files with `@JS` annotations
+2. Process any TypeScript definition files
+3. Generate Swift binding code in a `Generated` directory within your source folder
+
+For example, with a target named "MyApp", it will create:
+
+```
+Sources/MyApp/Generated/ExportSwift.swift # Generated code for Swift exports
+Sources/MyApp/Generated/ImportTS.swift # Generated code for TypeScript imports
+Sources/MyApp/Generated/JavaScript/ # Generated JSON skeletons
+```
+
+### Step 5: Add Generated Files to Version Control
+
+Add these generated files to your version control system:
+
+```bash
+git add Sources/MyApp/Generated
+git commit -m "Add generated BridgeJS code"
+```
+
+### Step 6: Build Your Package
+
+Now you can build your package as usual:
+
+```bash
+swift package --swift-sdk $SWIFT_SDK_ID js
+```
+
+Since the bridge code is already generated, the build will be faster.
+
+## Options for Selective Code Generation
+
+The command plugin supports targeting specific modules in your package:
+
+```bash
+# Generate bridge code only for the specified target
+swift package plugin bridge-js --target MyApp
+```
+
+## Updating Generated Code
+
+When you change your Swift code or TypeScript definitions, you'll need to regenerate the bridge code:
+
+```bash
+# Regenerate bridge code
+swift package plugin bridge-js
+git add Sources/MyApp/Generated
+git commit -m "Update generated BridgeJS code"
+```
+
+## When to Use Each Approach
+
+**Use the build plugin** when:
+- You're developing a small project or prototype
+- You frequently change your API boundaries
+- You want the simplest setup
+
+**Use the command plugin** when:
+- You're developing a larger project
+- Build time is a concern
+- You want to inspect and version control the generated code
+- You're working in a team and want to ensure consistent builds
+
+## Best Practices
+
+1. **Consistency**: Choose either the build plugin or the command plugin approach for your project
+2. **Version Control**: Always commit the generated files if using the command plugin
+3. **API Boundaries**: Try to stabilize your API boundaries to minimize regeneration
+4. **Documentation**: Document your approach in your project README
+5. **CI/CD**: If using the command plugin, consider verifying that generated code is up-to-date in CI
diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/Exporting-Swift-to-JavaScript.md b/Sources/JavaScriptKit/Documentation.docc/Articles/Exporting-Swift-to-JavaScript.md
new file mode 100644
index 00000000..08504c08
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Articles/Exporting-Swift-to-JavaScript.md
@@ -0,0 +1,164 @@
+# Exporting Swift to JavaScript
+
+Learn how to make your Swift code callable from JavaScript.
+
+## Overview
+
+> Important: This feature is still experimental. No API stability is guaranteed, and the API may change in future releases.
+
+BridgeJS allows you to expose Swift functions, classes, and methods to JavaScript by using the `@JS` attribute. This enables JavaScript code to call into Swift code running in WebAssembly.
+
+## Configuring the BridgeJS plugin
+
+To use the BridgeJS feature, you need to enable the experimental `Extern` feature and add the BridgeJS plugin to your package. Here's an example of a `Package.swift` file:
+
+```swift
+// swift-tools-version:6.0
+
+import PackageDescription
+
+let package = Package(
+ name: "MyApp",
+ dependencies: [
+ .package(url: "https://github.com/swiftwasm/JavaScriptKit.git", branch: "main")
+ ],
+ targets: [
+ .executableTarget(
+ name: "MyApp",
+ dependencies: ["JavaScriptKit"],
+ swiftSettings: [
+ // This is required because the generated code depends on @_extern(wasm)
+ .enableExperimentalFeature("Extern")
+ ],
+ plugins: [
+ // Add build plugin for processing @JS and generate Swift glue code
+ .plugin(name: "BridgeJS", package: "JavaScriptKit")
+ ]
+ )
+ ]
+)
+```
+
+The `BridgeJS` plugin will process your Swift code to find declarations marked with `@JS` and generate the necessary bridge code to make them accessible from JavaScript.
+
+### Building your package for JavaScript
+
+After configuring your `Package.swift`, you can build your package for JavaScript using the following command:
+
+```bash
+swift package --swift-sdk $SWIFT_SDK_ID js
+```
+
+This command will:
+1. Process all Swift files with `@JS` annotations
+2. Generate JavaScript bindings and TypeScript type definitions (`.d.ts`) for your exported Swift code
+4. Output everything to the `.build/plugins/PackageToJS/outputs/` directory
+
+> Note: For larger projects, you may want to generate the BridgeJS code ahead of time to improve build performance. See for more information.
+
+## Marking Swift Code for Export
+
+### Functions
+
+To export a Swift function to JavaScript, mark it with the `@JS` attribute and make it `public`:
+
+```swift
+import JavaScriptKit
+
+@JS public func calculateTotal(price: Double, quantity: Int) -> Double {
+ return price * Double(quantity)
+}
+
+@JS public func formatCurrency(amount: Double) -> String {
+ return "$\(String(format: "%.2f", amount))"
+}
+```
+
+These functions will be accessible from JavaScript:
+
+```javascript
+const total = exports.calculateTotal(19.99, 3);
+const formattedTotal = exports.formatCurrency(total);
+console.log(formattedTotal); // "$59.97"
+```
+
+The generated TypeScript declarations for these functions would look like:
+
+```typescript
+export type Exports = {
+ calculateTotal(price: number, quantity: number): number;
+ formatCurrency(amount: number): string;
+}
+```
+
+### Classes
+
+To export a Swift class, mark both the class and any members you want to expose:
+
+```swift
+import JavaScriptKit
+
+@JS class ShoppingCart {
+ private var items: [(name: String, price: Double, quantity: Int)] = []
+
+ @JS init() {}
+
+ @JS public func addItem(name: String, price: Double, quantity: Int) {
+ items.append((name, price, quantity))
+ }
+
+ @JS public func removeItem(atIndex index: Int) {
+ guard index >= 0 && index < items.count else { return }
+ items.remove(at: index)
+ }
+
+ @JS public func getTotal() -> Double {
+ return items.reduce(0) { $0 + $1.price * Double($1.quantity) }
+ }
+
+ @JS public func getItemCount() -> Int {
+ return items.count
+ }
+
+ // This method won't be accessible from JavaScript (no @JS)
+ var debugDescription: String {
+ return "Cart with \(items.count) items, total: \(getTotal())"
+ }
+}
+```
+
+In JavaScript:
+
+```javascript
+import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
+const { exports } = await init({});
+
+const cart = new exports.ShoppingCart();
+cart.addItem("Laptop", 999.99, 1);
+cart.addItem("Mouse", 24.99, 2);
+console.log(`Items in cart: ${cart.getItemCount()}`);
+console.log(`Total: $${cart.getTotal().toFixed(2)}`);
+```
+
+The generated TypeScript declarations for this class would look like:
+
+```typescript
+// Base interface for Swift reference types
+export interface SwiftHeapObject {
+ release(): void;
+}
+
+// ShoppingCart interface with all exported methods
+export interface ShoppingCart extends SwiftHeapObject {
+ addItem(name: string, price: number, quantity: number): void;
+ removeItem(atIndex: number): void;
+ getTotal(): number;
+ getItemCount(): number;
+}
+
+export type Exports = {
+ ShoppingCart: {
+ new(): ShoppingCart;
+ }
+}
+```
diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/Importing-TypeScript-into-Swift.md b/Sources/JavaScriptKit/Documentation.docc/Articles/Importing-TypeScript-into-Swift.md
new file mode 100644
index 00000000..5f9bb4a1
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Articles/Importing-TypeScript-into-Swift.md
@@ -0,0 +1,174 @@
+# Importing TypeScript into Swift
+
+Learn how to leverage TypeScript definitions to create type-safe bindings for JavaScript APIs in your Swift code.
+
+## Overview
+
+> Important: This feature is still experimental. No API stability is guaranteed, and the API may change in future releases.
+
+BridgeJS enables seamless integration between Swift and JavaScript by automatically generating Swift bindings from TypeScript declaration files (`.d.ts`). This provides type-safe access to JavaScript APIs directly from your Swift code.
+
+The key benefits of this approach over `@dynamicMemberLookup`-based APIs include:
+
+- **Type Safety**: Catch errors at compile-time rather than runtime
+- **IDE Support**: Get autocompletion and documentation in your Swift editor
+- **Performance**: Eliminating dynamism allows us to optimize the glue code
+
+If you prefer keeping your project simple, you can continue using `@dynamicMemberLookup`-based APIs.
+
+## Getting Started
+
+### Step 1: Configure Your Package
+
+First, add the BridgeJS plugin to your Swift package by modifying your `Package.swift` file:
+
+```swift
+// swift-tools-version:6.0
+
+import PackageDescription
+
+let package = Package(
+ name: "MyApp",
+ dependencies: [
+ .package(url: "https://github.com/swiftwasm/JavaScriptKit.git", branch: "main")
+ ],
+ targets: [
+ .executableTarget(
+ name: "MyApp",
+ dependencies: ["JavaScriptKit"],
+ swiftSettings: [
+ // This is required because the generated code depends on @_extern(wasm)
+ .enableExperimentalFeature("Extern")
+ ],
+ plugins: [
+ // Add build plugin for processing @JS and generate Swift glue code
+ .plugin(name: "BridgeJS", package: "JavaScriptKit")
+ ]
+ )
+ ]
+)
+```
+
+### Step 2: Create TypeScript Definitions
+
+Create a file named `bridge.d.ts` in your target source directory (e.g. `Sources//bridge.d.ts`). This file defines the JavaScript APIs you want to use in Swift:
+
+```typescript
+// Simple function
+export function consoleLog(message: string): void;
+
+// Define a subset of DOM API you want to use
+interface Document {
+ // Properties
+ title: string;
+ readonly body: HTMLElement;
+
+ // Methods
+ getElementById(id: string): HTMLElement;
+ createElement(tagName: string): HTMLElement;
+}
+
+// You can use type-level operations like `Pick` to reuse
+// type definitions provided by `lib.dom.d.ts`.
+interface HTMLElement extends Pick {
+ appendChild(child: HTMLElement): void;
+ // TODO: Function types on function signatures are not supported yet.
+ // addEventListener(event: string, handler: (event: any) => void): void;
+}
+
+// Provide access to `document`
+export function getDocument(): Document;
+```
+
+BridgeJS will generate Swift code that matches these TypeScript declarations. For example:
+
+```swift
+func consoleLog(message: String)
+
+struct Document {
+ var title: String { get set }
+ var body: HTMLElement { get }
+
+ func getElementById(_ id: String) -> HTMLElement
+ func createElement(_ tagName: String) -> HTMLElement
+}
+
+struct HTMLElement {
+ var innerText: String { get set }
+ var className: String { get set }
+
+ func appendChild(_ child: HTMLElement)
+}
+
+func getDocument() -> Document
+```
+
+### Step 3: Build Your Package
+
+Build your package with the following command:
+
+```bash
+swift package --swift-sdk $SWIFT_SDK_ID js
+```
+
+This command:
+1. Processes your TypeScript definition files
+2. Generates corresponding Swift bindings
+3. Compiles your Swift code to WebAssembly
+4. Produces JavaScript glue code in `.build/plugins/PackageToJS/outputs/`
+
+> Note: For larger projects, you may want to generate the BridgeJS code ahead of time to improve build performance. See for more information.
+
+### Step 4: Use the Generated Swift Bindings
+
+The BridgeJS plugin automatically generates Swift bindings that match your TypeScript definitions. You can now use these APIs directly in your Swift code:
+
+```swift
+import JavaScriptKit
+
+@JS func run() {
+ // Simple function call
+ consoleLog("Hello from Swift!")
+
+ // Get `document`
+ let document = getDocument()
+
+ // Property access
+ document.title = "My Swift App"
+
+ // Method calls
+ let button = document.createElement("button")
+ button.innerText = "Click Me"
+
+ // TODO: Function types on function signatures are not supported yet.
+ // buttion.addEventListener("click") { _ in
+ // print("On click!")
+ // }
+
+ // DOM manipulation
+ let container = document.getElementById("app")
+ container.appendChild(button)
+}
+```
+
+### Step 5: Inject JavaScript Implementations
+
+The final step is to provide the actual JavaScript implementations for the TypeScript declarations you defined. You need to create a JavaScript file that initializes your WebAssembly module with the appropriate implementations:
+
+```javascript
+// index.js
+import { init } from "./.build/plugins/PackageToJS/outputs/Package/index.js";
+
+// Initialize the WebAssembly module with JavaScript implementations
+const { exports } = await init({
+ imports: {
+ consoleLog: (message) => {
+ console.log(message);
+ },
+ getDocument: () => document,
+ }
+});
+
+// Call the entry point of your Swift application
+exports.run();
+```
diff --git a/Sources/JavaScriptKit/Documentation.docc/Documentation.md b/Sources/JavaScriptKit/Documentation.docc/Documentation.md
index 94d5ba3c..ffc16843 100644
--- a/Sources/JavaScriptKit/Documentation.docc/Documentation.md
+++ b/Sources/JavaScriptKit/Documentation.docc/Documentation.md
@@ -49,8 +49,16 @@ Check out the [examples](https://github.com/swiftwasm/JavaScriptKit/tree/main/Ex
-
-### Core Types
+### Articles
--
--
--
+-
+-
+-
+-
+-
+
+### Core APIs
+
+- ``JSValue``
+- ``JSObject``
+- ``JS()``
diff --git a/Sources/JavaScriptKit/Macros.swift b/Sources/JavaScriptKit/Macros.swift
new file mode 100644
index 00000000..bddd8c7c
--- /dev/null
+++ b/Sources/JavaScriptKit/Macros.swift
@@ -0,0 +1,35 @@
+/// A macro that exposes Swift functions, classes, and methods to JavaScript.
+///
+/// Apply this macro to Swift declarations that you want to make callable from JavaScript:
+///
+/// ```swift
+/// // Export a function to JavaScript
+/// @JS public func greet(name: String) -> String {
+/// return "Hello, \(name)!"
+/// }
+///
+/// // Export a class and its members
+/// @JS class Counter {
+/// private var count = 0
+///
+/// @JS init() {}
+///
+/// @JS func increment() {
+/// count += 1
+/// }
+///
+/// @JS func getValue() -> Int {
+/// return count
+/// }
+/// }
+/// ```
+///
+/// When you build your project with the BridgeJS plugin, these declarations will be
+/// accessible from JavaScript, and TypeScript declaration files (`.d.ts`) will be
+/// automatically generated to provide type safety.
+///
+/// For detailed usage information, see the article .
+///
+/// - Important: This feature is still experimental. No API stability is guaranteed, and the API may change in future releases.
+@attached(peer)
+public macro JS() = Builtin.ExternalMacro
diff --git a/Tests/BridgeJSRuntimeTests/ExportAPITests.swift b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift
new file mode 100644
index 00000000..1473594e
--- /dev/null
+++ b/Tests/BridgeJSRuntimeTests/ExportAPITests.swift
@@ -0,0 +1,61 @@
+import XCTest
+import JavaScriptKit
+
+@_extern(wasm, module: "BridgeJSRuntimeTests", name: "runJsWorks")
+@_extern(c)
+func runJsWorks() -> Void
+
+@JS func roundTripInt(v: Int) -> Int {
+ return v
+}
+@JS func roundTripFloat(v: Float) -> Float {
+ return v
+}
+@JS func roundTripDouble(v: Double) -> Double {
+ return v
+}
+@JS func roundTripBool(v: Bool) -> Bool {
+ return v
+}
+@JS func roundTripString(v: String) -> String {
+ return v
+}
+@JS func roundTripSwiftHeapObject(v: Greeter) -> Greeter {
+ return v
+}
+
+@JS class Greeter {
+ var name: String
+
+ nonisolated(unsafe) static var onDeinit: () -> Void = {}
+
+ @JS init(name: String) {
+ self.name = name
+ }
+
+ @JS func greet() -> String {
+ return "Hello, \(name)!"
+ }
+ @JS func changeName(name: String) {
+ self.name = name
+ }
+
+ deinit {
+ Self.onDeinit()
+ }
+}
+
+@JS func takeGreeter(g: Greeter, name: String) {
+ g.changeName(name: name)
+}
+
+class ExportAPITests: XCTestCase {
+ func testAll() {
+ var hasDeinitGreeter = false
+ Greeter.onDeinit = {
+ hasDeinitGreeter = true
+ }
+ runJsWorks()
+ XCTAssertTrue(hasDeinitGreeter)
+ }
+}
diff --git a/Tests/BridgeJSRuntimeTests/Generated/ExportSwift.swift b/Tests/BridgeJSRuntimeTests/Generated/ExportSwift.swift
new file mode 100644
index 00000000..cc3c9df3
--- /dev/null
+++ b/Tests/BridgeJSRuntimeTests/Generated/ExportSwift.swift
@@ -0,0 +1,98 @@
+@_extern(wasm, module: "bjs", name: "return_string")
+private func _return_string(_ ptr: UnsafePointer?, _ len: Int32)
+@_extern(wasm, module: "bjs", name: "init_memory")
+private func _init_memory(_ sourceId: Int32, _ ptr: UnsafeMutablePointer?)
+
+@_expose(wasm, "bjs_roundTripInt")
+@_cdecl("bjs_roundTripInt")
+public func _bjs_roundTripInt(v: Int32) -> Int32 {
+ let ret = roundTripInt(v: Int(v))
+ return Int32(ret)
+}
+
+@_expose(wasm, "bjs_roundTripFloat")
+@_cdecl("bjs_roundTripFloat")
+public func _bjs_roundTripFloat(v: Float32) -> Float32 {
+ let ret = roundTripFloat(v: v)
+ return Float32(ret)
+}
+
+@_expose(wasm, "bjs_roundTripDouble")
+@_cdecl("bjs_roundTripDouble")
+public func _bjs_roundTripDouble(v: Float64) -> Float64 {
+ let ret = roundTripDouble(v: v)
+ return Float64(ret)
+}
+
+@_expose(wasm, "bjs_roundTripBool")
+@_cdecl("bjs_roundTripBool")
+public func _bjs_roundTripBool(v: Int32) -> Int32 {
+ let ret = roundTripBool(v: v == 1)
+ return Int32(ret ? 1 : 0)
+}
+
+@_expose(wasm, "bjs_roundTripString")
+@_cdecl("bjs_roundTripString")
+public func _bjs_roundTripString(vBytes: Int32, vLen: Int32) -> Void {
+ let v = String(unsafeUninitializedCapacity: Int(vLen)) { b in
+ _init_memory(vBytes, b.baseAddress.unsafelyUnwrapped)
+ return Int(vLen)
+ }
+ var ret = roundTripString(v: v)
+ return ret.withUTF8 { ptr in
+ _return_string(ptr.baseAddress, Int32(ptr.count))
+ }
+}
+
+@_expose(wasm, "bjs_roundTripSwiftHeapObject")
+@_cdecl("bjs_roundTripSwiftHeapObject")
+public func _bjs_roundTripSwiftHeapObject(v: UnsafeMutableRawPointer) -> UnsafeMutableRawPointer {
+ let ret = roundTripSwiftHeapObject(v: Unmanaged.fromOpaque(v).takeUnretainedValue())
+ return Unmanaged.passRetained(ret).toOpaque()
+}
+
+@_expose(wasm, "bjs_takeGreeter")
+@_cdecl("bjs_takeGreeter")
+public func _bjs_takeGreeter(g: UnsafeMutableRawPointer, nameBytes: Int32, nameLen: Int32) -> Void {
+ let name = String(unsafeUninitializedCapacity: Int(nameLen)) { b in
+ _init_memory(nameBytes, b.baseAddress.unsafelyUnwrapped)
+ return Int(nameLen)
+ }
+ takeGreeter(g: Unmanaged.fromOpaque(g).takeUnretainedValue(), name: name)
+}
+
+@_expose(wasm, "bjs_Greeter_init")
+@_cdecl("bjs_Greeter_init")
+public func _bjs_Greeter_init(nameBytes: Int32, nameLen: Int32) -> UnsafeMutableRawPointer {
+ let name = String(unsafeUninitializedCapacity: Int(nameLen)) { b in
+ _init_memory(nameBytes, b.baseAddress.unsafelyUnwrapped)
+ return Int(nameLen)
+ }
+ let ret = Greeter(name: name)
+ return Unmanaged.passRetained(ret).toOpaque()
+}
+
+@_expose(wasm, "bjs_Greeter_greet")
+@_cdecl("bjs_Greeter_greet")
+public func _bjs_Greeter_greet(_self: UnsafeMutableRawPointer) -> Void {
+ var ret = Unmanaged.fromOpaque(_self).takeUnretainedValue().greet()
+ return ret.withUTF8 { ptr in
+ _return_string(ptr.baseAddress, Int32(ptr.count))
+ }
+}
+
+@_expose(wasm, "bjs_Greeter_changeName")
+@_cdecl("bjs_Greeter_changeName")
+public func _bjs_Greeter_changeName(_self: UnsafeMutableRawPointer, nameBytes: Int32, nameLen: Int32) -> Void {
+ let name = String(unsafeUninitializedCapacity: Int(nameLen)) { b in
+ _init_memory(nameBytes, b.baseAddress.unsafelyUnwrapped)
+ return Int(nameLen)
+ }
+ Unmanaged.fromOpaque(_self).takeUnretainedValue().changeName(name: name)
+}
+
+@_expose(wasm, "bjs_Greeter_deinit")
+@_cdecl("bjs_Greeter_deinit")
+public func _bjs_Greeter_deinit(pointer: UnsafeMutableRawPointer) {
+ Unmanaged.fromOpaque(pointer).release()
+}
\ No newline at end of file
diff --git a/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ExportSwift.json b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ExportSwift.json
new file mode 100644
index 00000000..f60426a0
--- /dev/null
+++ b/Tests/BridgeJSRuntimeTests/Generated/JavaScript/ExportSwift.json
@@ -0,0 +1,206 @@
+{
+ "classes" : [
+ {
+ "constructor" : {
+ "abiName" : "bjs_Greeter_init",
+ "parameters" : [
+ {
+ "label" : "name",
+ "name" : "name",
+ "type" : {
+ "string" : {
+
+ }
+ }
+ }
+ ]
+ },
+ "methods" : [
+ {
+ "abiName" : "bjs_Greeter_greet",
+ "name" : "greet",
+ "parameters" : [
+
+ ],
+ "returnType" : {
+ "string" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_Greeter_changeName",
+ "name" : "changeName",
+ "parameters" : [
+ {
+ "label" : "name",
+ "name" : "name",
+ "type" : {
+ "string" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "void" : {
+
+ }
+ }
+ }
+ ],
+ "name" : "Greeter"
+ }
+ ],
+ "functions" : [
+ {
+ "abiName" : "bjs_roundTripInt",
+ "name" : "roundTripInt",
+ "parameters" : [
+ {
+ "label" : "v",
+ "name" : "v",
+ "type" : {
+ "int" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "int" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_roundTripFloat",
+ "name" : "roundTripFloat",
+ "parameters" : [
+ {
+ "label" : "v",
+ "name" : "v",
+ "type" : {
+ "float" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "float" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_roundTripDouble",
+ "name" : "roundTripDouble",
+ "parameters" : [
+ {
+ "label" : "v",
+ "name" : "v",
+ "type" : {
+ "double" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "double" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_roundTripBool",
+ "name" : "roundTripBool",
+ "parameters" : [
+ {
+ "label" : "v",
+ "name" : "v",
+ "type" : {
+ "bool" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "bool" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_roundTripString",
+ "name" : "roundTripString",
+ "parameters" : [
+ {
+ "label" : "v",
+ "name" : "v",
+ "type" : {
+ "string" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "string" : {
+
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_roundTripSwiftHeapObject",
+ "name" : "roundTripSwiftHeapObject",
+ "parameters" : [
+ {
+ "label" : "v",
+ "name" : "v",
+ "type" : {
+ "swiftHeapObject" : {
+ "_0" : "Greeter"
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "swiftHeapObject" : {
+ "_0" : "Greeter"
+ }
+ }
+ },
+ {
+ "abiName" : "bjs_takeGreeter",
+ "name" : "takeGreeter",
+ "parameters" : [
+ {
+ "label" : "g",
+ "name" : "g",
+ "type" : {
+ "swiftHeapObject" : {
+ "_0" : "Greeter"
+ }
+ }
+ },
+ {
+ "label" : "name",
+ "name" : "name",
+ "type" : {
+ "string" : {
+
+ }
+ }
+ }
+ ],
+ "returnType" : {
+ "void" : {
+
+ }
+ }
+ }
+ ]
+}
\ No newline at end of file
diff --git a/Tests/prelude.mjs b/Tests/prelude.mjs
index ab572358..1e12d375 100644
--- a/Tests/prelude.mjs
+++ b/Tests/prelude.mjs
@@ -4,15 +4,71 @@ export function setupOptions(options, context) {
setupTestGlobals(globalThis);
return {
...options,
- addToCoreImports(importObject) {
+ addToCoreImports(importObject, getInstance, getExports) {
options.addToCoreImports?.(importObject);
importObject["JavaScriptEventLoopTestSupportTests"] = {
"isMainThread": () => context.isMainThread,
}
+ importObject["BridgeJSRuntimeTests"] = {
+ "runJsWorks": () => {
+ return BridgeJSRuntimeTests_runJsWorks(getInstance(), getExports());
+ },
+ }
}
}
}
+import assert from "node:assert";
+
+/** @param {import('./../.build/plugins/PackageToJS/outputs/PackageTests/bridge.d.ts').Exports} exports */
+function BridgeJSRuntimeTests_runJsWorks(instance, exports) {
+ for (const v of [0, 1, -1, 2147483647, -2147483648]) {
+ assert.equal(exports.roundTripInt(v), v);
+ }
+ for (const v of [
+ 0.0, 1.0, -1.0,
+ NaN,
+ Infinity,
+ /* .pi */ 3.141592502593994,
+ /* .greatestFiniteMagnitude */ 3.4028234663852886e+38,
+ /* .leastNonzeroMagnitude */ 1.401298464324817e-45
+ ]) {
+ assert.equal(exports.roundTripFloat(v), v);
+ }
+ for (const v of [
+ 0.0, 1.0, -1.0,
+ NaN,
+ Infinity,
+ /* .pi */ 3.141592502593994,
+ /* .greatestFiniteMagnitude */ 3.4028234663852886e+38,
+ /* .leastNonzeroMagnitude */ 1.401298464324817e-45
+ ]) {
+ assert.equal(exports.roundTripDouble(v), v);
+ }
+ for (const v of [true, false]) {
+ assert.equal(exports.roundTripBool(v), v);
+ }
+ for (const v of [
+ "Hello, world!",
+ "๐",
+ "ใใใซใกใฏ",
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
+ ]) {
+ assert.equal(exports.roundTripString(v), v);
+ }
+
+ const g = new exports.Greeter("John");
+ const g2 = exports.roundTripSwiftHeapObject(g)
+ g2.release();
+
+ assert.equal(g.greet(), "Hello, John!");
+ g.changeName("Jane");
+ assert.equal(g.greet(), "Hello, Jane!");
+ exports.takeGreeter(g, "Jay");
+ assert.equal(g.greet(), "Hello, Jay!");
+ g.release();
+}
+
function setupTestGlobals(global) {
global.globalObject1 = {
prop_1: {
diff --git a/Utilities/format.swift b/Utilities/format.swift
index be6e7085..9df282ad 100755
--- a/Utilities/format.swift
+++ b/Utilities/format.swift
@@ -63,6 +63,7 @@ let excluded: Set = [
".index-build",
"node_modules",
"__Snapshots__",
+ "Generated",
// Exclude the script itself to avoid changing its file mode.
URL(fileURLWithPath: #filePath).lastPathComponent,
]