Skip to content

Add BridgeJS playground #388

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Jul 23, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -88,3 +88,36 @@ jobs:
echo "::error::The formatting changed some files. Please run \`./Utilities/format.swift\` and commit the changes."
exit 1
}
build-examples:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: ./.github/actions/install-swift
with:
download-url: https://download.swift.org/development/ubuntu2204/swift-DEVELOPMENT-SNAPSHOT-2025-06-12-a/swift-DEVELOPMENT-SNAPSHOT-2025-06-12-a-ubuntu22.04.tar.gz
- uses: swiftwasm/setup-swiftwasm@v2
id: setup-wasm32-unknown-wasi
with: { target: wasm32-unknown-wasi }
- uses: swiftwasm/setup-swiftwasm@v2
id: setup-wasm32-unknown-wasip1-threads
with: { target: wasm32-unknown-wasip1-threads }
- run: ./Utilities/build-examples.sh
env:
SWIFT_SDK_ID_wasm32_unknown_wasip1_threads: ${{ steps.setup-wasm32-unknown-wasip1-threads.outputs.swift-sdk-id }}
SWIFT_SDK_ID_wasm32_unknown_wasi: ${{ steps.setup-wasm32-unknown-wasi.outputs.swift-sdk-id }}
- name: Upload static files as artifact
id: deployment
uses: actions/upload-pages-artifact@v3
with:
path: Examples/
deploy-examples:
runs-on: ubuntu-latest
if: github.event_name == 'push' && github.ref == 'refs/heads/main'
environment:
name: github-pages
url: ${{ steps.deployment.outputs.page_url }}
needs: build-examples
steps:
- name: Deploy to GitHub Pages
id: deployment
uses: actions/deploy-pages@v4
4 changes: 3 additions & 1 deletion Examples/ActorOnWebWorker/build.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
swift package --swift-sdk "${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}" -c release \
#!/bin/bash
set -euxo pipefail
swift package --swift-sdk "${SWIFT_SDK_ID_wasm32_unknown_wasip1_threads:-${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}}" -c release \
plugin --allow-writing-to-package-directory \
js --use-cdn --output ./Bundle
4 changes: 2 additions & 2 deletions Examples/Basic/build.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,3 @@
#!/bin/bash
set -ex
swift package --swift-sdk "${SWIFT_SDK_ID:-wasm32-unknown-wasi}" -c "${1:-debug}" js --use-cdn
set -euxo pipefail
swift package --swift-sdk "${SWIFT_SDK_ID_wasm32_unknown_wasi:-${SWIFT_SDK_ID:-wasm32-unknown-wasi}}" -c "${1:-debug}" js --use-cdn
1 change: 1 addition & 0 deletions Examples/Embedded/build.sh
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
#!/bin/bash
set -euxo pipefail
package_dir="$(cd "$(dirname "$0")" && pwd)"
JAVASCRIPTKIT_EXPERIMENTAL_EMBEDDED_WASM=true \
swift package --package-path "$package_dir" \
Expand Down
4 changes: 3 additions & 1 deletion Examples/Multithreading/build.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
swift package --swift-sdk "${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}" -c release \
#!/bin/bash
set -euxo pipefail
swift package --swift-sdk "${SWIFT_SDK_ID_wasm32_unknown_wasip1_threads:-${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}}" -c release \
plugin --allow-writing-to-package-directory \
js --use-cdn --output ./Bundle
4 changes: 3 additions & 1 deletion Examples/OffscrenCanvas/build.sh
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
swift package --swift-sdk "${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}" -c release \
#!/bin/bash
set -euxo pipefail
swift package --swift-sdk "${SWIFT_SDK_ID_wasm32_unknown_wasip1_threads:-${SWIFT_SDK_ID:-wasm32-unknown-wasip1-threads}}" -c release \
plugin --allow-writing-to-package-directory \
js --use-cdn --output ./Bundle
40 changes: 40 additions & 0 deletions Examples/PlayBridgeJS/Package.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// swift-tools-version:6.0

import PackageDescription

let package = Package(
name: "PlayBridgeJS",
platforms: [
.macOS(.v14)
],
dependencies: [
.package(name: "JavaScriptKit", path: "../../"),
.package(url: "https://github.com/swiftlang/swift-syntax", from: "600.0.1"),
],
targets: [
.executableTarget(
name: "PlayBridgeJS",
dependencies: [
"JavaScriptKit",
.product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
.product(name: "SwiftParser", package: "swift-syntax"),
.product(name: "SwiftSyntax", package: "swift-syntax"),
.product(name: "SwiftBasicFormat", package: "swift-syntax"),
.product(name: "SwiftSyntaxBuilder", package: "swift-syntax"),
],
swiftSettings: [
.enableExperimentalFeature("Extern")
],
linkerSettings: [
.unsafeFlags(
[
"-Xlinker", "--stack-first",
"-Xlinker", "--global-base=524288",
"-Xlinker", "-z", "-Xlinker", "stack-size=524288",
],
.when(platforms: [.wasi])
)
]
)
]
)
9 changes: 9 additions & 0 deletions Examples/PlayBridgeJS/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# Playground for BridgeJS example

Install Development Snapshot toolchain `DEVELOPMENT-SNAPSHOT-2024-07-08-a` from [swift.org/install](https://www.swift.org/install/) and run the following commands:

```sh
$ swift sdk install https://github.com/swiftwasm/swift/releases/download/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a/swift-wasm-DEVELOPMENT-SNAPSHOT-2024-07-09-a-wasm32-unknown-wasi.artifactbundle.zip
$ ./build.sh
$ npx serve
```
165 changes: 165 additions & 0 deletions Examples/PlayBridgeJS/Sources/JavaScript/app.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,165 @@
// BridgeJS Playground Main Application
import { EditorSystem } from './editor.js';
import ts from 'typescript';
import { TypeProcessor } from './processor.js';

export class BridgeJSPlayground {
constructor() {
this.editorSystem = new EditorSystem();
this.playBridgeJS = null;
this.generateTimeout = null;
this.isInitialized = false;

// DOM Elements
this.errorDisplay = document.getElementById('errorDisplay');
this.errorMessage = document.getElementById('errorMessage');
}

// Initialize the application
async initialize() {
if (this.isInitialized) {
return;
}

try {
// Initialize editor system
await this.editorSystem.init();

// Initialize BridgeJS
await this.initializeBridgeJS();

// Set up event listeners
this.setupEventListeners();

// Load sample code
this.editorSystem.loadSampleCode();

this.isInitialized = true;
console.log('BridgeJS Playground initialized successfully');
} catch (error) {
console.error('Failed to initialize BridgeJS Playground:', error);
this.showError('Failed to initialize application: ' + error.message);
}
}

// Initialize BridgeJS
async initializeBridgeJS() {
try {
// Import the BridgeJS module
const { init } = await import("../../.build/plugins/PackageToJS/outputs/Package/index.js");
const { exports } = await init({
imports: {
createTS2Skeleton: this.createTS2Skeleton
}
});
this.playBridgeJS = new exports.PlayBridgeJS();
console.log('BridgeJS initialized successfully');
} catch (error) {
console.error('Failed to initialize BridgeJS:', error);
throw new Error('BridgeJS initialization failed: ' + error.message);
}
}

// Set up event listeners
setupEventListeners() {
// Add change listeners for real-time generation
this.editorSystem.addChangeListeners(() => {
// Debounce generation to avoid excessive calls
if (this.generateTimeout) {
clearTimeout(this.generateTimeout);
}
this.generateTimeout = setTimeout(() => this.generateCode(), 300);
});
}

createTS2Skeleton() {
return {
convert: (dtsCode) => {
const virtualFilePath = "bridge-js.d.ts"
const virtualHost = {
fileExists: fileName => fileName === virtualFilePath,
readFile: fileName => dtsCode,
getSourceFile: (fileName, languageVersion) => {
const sourceText = dtsCode;
if (sourceText === undefined) return undefined;
return ts.createSourceFile(fileName, sourceText, languageVersion);
},
getDefaultLibFileName: options => "lib.d.ts",
writeFile: (fileName, data) => {
console.log(`[emit] ${fileName}:\n${data}`);
},
getCurrentDirectory: () => "",
getDirectories: () => [],
getCanonicalFileName: fileName => fileName,
getNewLine: () => "\n",
useCaseSensitiveFileNames: () => true
}
// Create TypeScript program from d.ts content
const tsProgram = ts.createProgram({
rootNames: [virtualFilePath],
host: virtualHost,
options: {
noEmit: true,
declaration: true,
}
})

// Create diagnostic engine for error reporting
const diagnosticEngine = {
print: (level, message, node) => {
console.log(`[${level}] ${message}`);
if (level === 'error') {
this.showError(`TypeScript Error: ${message}`);
}
}
};

// Process the TypeScript definitions to generate skeleton
const processor = new TypeProcessor(tsProgram.getTypeChecker(), diagnosticEngine);

const skeleton = processor.processTypeDeclarations(tsProgram, virtualFilePath);

return JSON.stringify(skeleton);
}
}
}

// Generate code through BridgeJS
async generateCode() {
if (!this.playBridgeJS) {
this.showError('BridgeJS is not initialized');
return;
}

try {
this.hideError();

const inputs = this.editorSystem.getInputs();
const swiftCode = inputs.swift;
const dtsCode = inputs.dts;

// Process the code and get PlayBridgeJSOutput
const result = this.playBridgeJS.update(swiftCode, dtsCode);

// Update outputs using the PlayBridgeJSOutput object
this.editorSystem.updateOutputs(result);

console.log('Code generated successfully');

} catch (error) {
console.error('Error generating code:', error);
this.showError('Error generating code: ' + error.message);
}
}

// Show error message
showError(message) {
this.errorMessage.textContent = message;
this.errorDisplay.classList.add('show');
}

// Hide error message
hideError() {
this.errorDisplay.classList.remove('show');
}
}
Loading