diff --git a/.gitignore b/.gitignore
index 1d3cb87be..2fb37cb48 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ xcuserdata/
.vscode
Examples/*/Bundle
Examples/*/package-lock.json
+/Package.resolved
diff --git a/Package.swift b/Package.swift
index 7c49f0e33..173add2dd 100644
--- a/Package.swift
+++ b/Package.swift
@@ -83,3 +83,9 @@ let package = Package(
),
]
)
+
+if Context.environment["JAVASCRIPTKIT_USE_DOCC_PLUGIN"] != nil {
+ package.dependencies.append(
+ .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.4.0")
+ )
+}
diff --git a/README.md b/README.md
index e7a9b63a5..c03561587 100644
--- a/README.md
+++ b/README.md
@@ -4,262 +4,51 @@
Swift framework to interact with JavaScript through WebAssembly.
-## Getting started
+## Quick Start
-This JavaScript code
+Check out the [Hello World](https://swiftpackageindex.com/swiftwasm/JavaScriptKit/main/tutorials/javascriptkit/hello-world) tutorial for a step-by-step guide to getting started.
-```javascript
-const alert = window.alert;
-const document = window.document;
+## Overview
-const divElement = document.createElement("div");
-divElement.innerText = "Hello, world";
-const body = document.body;
-body.appendChild(divElement);
+JavaScriptKit provides a seamless way to interact with JavaScript from Swift code when compiled to WebAssembly. It allows Swift developers to:
-const pet = {
- age: 3,
- owner: {
- name: "Mike",
- },
-};
-
-alert("JavaScript is running on browser!");
-```
-
-Can be written in Swift using JavaScriptKit
+- Access JavaScript objects and functions
+- Create closures that can be called from JavaScript
+- Convert between Swift and JavaScript data types
+- Use JavaScript promises with Swift's `async/await`
+- Work with multi-threading
```swift
import JavaScriptKit
+// Access global JavaScript objects
let document = JSObject.global.document
-var divElement = document.createElement("div")
-divElement.innerText = "Hello, world"
-_ = document.body.appendChild(divElement)
-
-struct Owner: Codable {
- let name: String
-}
-
-struct Pet: Codable {
- let age: Int
- let owner: Owner
-}
-
-let jsPet = JSObject.global.pet
-let swiftPet: Pet = try JSValueDecoder().decode(from: jsPet)
-
-_ = JSObject.global.alert!("Swift is running in the browser!")
-```
-
-### `async`/`await`
-
-Starting with SwiftWasm 5.5 you can use `async`/`await` with `JSPromise` objects. This requires
-a few additional steps though (you can skip these steps if your app depends on
-[Tokamak](https://tokamak.dev)):
-
-1. Make sure that your target depends on `JavaScriptEventLoop` in your `Packages.swift`:
-
-```swift
-.target(
- name: "JavaScriptKitExample",
- dependencies: [
- "JavaScriptKit",
- .product(name: "JavaScriptEventLoop", package: "JavaScriptKit"),
- ]
-)
-```
-
-2. Add an explicit import in the code that executes **before* you start using `await` and/or `Task`
-APIs (most likely in `main.swift`):
-
-```swift
-import JavaScriptEventLoop
-```
-
-3. Run this function **before* you start using `await` and/or `Task` APIs (again, most likely in
-`main.swift`):
-
-```swift
-JavaScriptEventLoop.installGlobalExecutor()
-```
-
-Then you can `await` on the `value` property of `JSPromise` instances, like in the example below:
-
-```swift
-import JavaScriptKit
-import JavaScriptEventLoop
-
-let alert = JSObject.global.alert.function!
-let document = JSObject.global.document
-
-private let jsFetch = JSObject.global.fetch.function!
-func fetch(_ url: String) -> JSPromise {
- JSPromise(jsFetch(url).object!)!
-}
-
-JavaScriptEventLoop.installGlobalExecutor()
-
-struct Response: Decodable {
- let uuid: String
-}
-
-var asyncButtonElement = document.createElement("button")
-asyncButtonElement.innerText = "Fetch UUID demo"
-asyncButtonElement.onclick = .object(JSClosure { _ in
- Task {
- do {
- let response = try await fetch("https://httpbin.org/uuid").value
- let json = try await JSPromise(response.json().object!)!.value
- let parsedResponse = try JSValueDecoder().decode(Response.self, from: json)
- alert(parsedResponse.uuid)
- } catch {
- print(error)
- }
- }
+// Create and manipulate DOM elements
+var div = document.createElement("div")
+div.innerText = "Hello from Swift!"
+_ = document.body.appendChild(div)
+// Handle events with Swift closures
+var button = document.createElement("button")
+button.innerText = "Click me"
+button.onclick = .object(JSClosure { _ in
+ JSObject.global.alert!("Button clicked!")
return .undefined
})
-
-_ = document.body.appendChild(asyncButtonElement)
-```
-
-### `JavaScriptEventLoop` activation in XCTest suites
-
-If you need to execute Swift async functions that can be resumed by JS event loop in your XCTest suites, please add `JavaScriptEventLoopTestSupport` to your test target dependencies.
-
-```diff
- .testTarget(
- name: "MyAppTests",
- dependencies: [
- "MyApp",
-+ "JavaScriptEventLoopTestSupport",
- ]
- )
-```
-
-Linking this module automatically activates JS event loop based global executor by calling `JavaScriptEventLoop.installGlobalExecutor()`
-
-
-## Requirements
-
-### For developers
-
-- macOS 11 and Xcode 13.2 or later versions, which support Swift Concurrency back-deployment.
-To use earlier versions of Xcode on macOS 11 you'll have to
-add `.unsafeFlags(["-Xfrontend", "-disable-availability-checking"])` in `Package.swift` manifest of
-your package that depends on JavaScriptKit. You can also use Xcode 13.0 and 13.1 on macOS Monterey,
-since this OS does not need back-deployment.
-- [Swift 5.5 or later](https://swift.org/download/) and Ubuntu 18.04 if you'd like to use Linux.
- Other Linux distributions are currently not supported.
-
-### For users of apps depending on JavaScriptKit
-
-Any recent browser that [supports WebAssembly](https://caniuse.com/#feat=wasm) and required
-JavaScript features should work, which currently includes:
-
-- Edge 84+
-- Firefox 79+
-- Chrome 84+
-- Desktop Safari 14.1+
-- Mobile Safari 14.8+
-
-If you need to support older browser versions, you'll have to build with
-the `JAVASCRIPTKIT_WITHOUT_WEAKREFS` flag, passing `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS` flags
-when compiling. This should lower browser requirements to these versions:
-
-- Edge 16+
-- Firefox 61+
-- Chrome 66+
-- (Mobile) Safari 12+
-
-Not all of these versions are tested on regular basis though, compatibility reports are very welcome!
-
-## Usage in a browser application
-
-The easiest is to start with [Examples](/Examples) which has JavaScript glue runtime.
-
-Second option is to get started with JavaScriptKit in your browser app is with [the `carton`
-bundler](https://carton.dev). Add carton to your swift package dependencies:
-
-```diff
-dependencies: [
-+ .package(url: "https://github.com/swiftwasm/carton", from: "1.1.3"),
-],
-```
-
-Now you can activate the package dependency through swift:
-
-```
-swift run carton dev
+_ = document.body.appendChild(button)
```
-If you have multiple products in your package, you can also used the product flag:
-
-```
-swift run carton dev --product MyApp
-```
+Check out the [examples](https://github.com/swiftwasm/JavaScriptKit/tree/main/Examples) for more detailed usage.
-> [!WARNING]
-> - If you already use `carton` before 0.x.x versions via Homebrew, you can remove it with `brew uninstall carton` and install the new version as a SwiftPM dependency.
-> - Also please remove the old `.build` directory before using the new `carton`
+## Contributing
-Legacy Installation
-
----
-
-As a part of these steps
-you'll install `carton` via [Homebrew](https://brew.sh/) on macOS (you can also use the
-[`ghcr.io/swiftwasm/carton`](https://github.com/orgs/swiftwasm/packages/container/package/carton)
-Docker image if you prefer to run the build steps on Linux). Assuming you already have Homebrew
-installed, you can create a new app that uses JavaScriptKit by following these steps:
-
-1. Install `carton`:
-
-```
-brew install swiftwasm/tap/carton
-```
-
-If you had `carton` installed before this, make sure you have version 0.6.1 or greater:
-
-```
-carton --version
-```
-
-2. Create a directory for your project and make it current:
-
-```
-mkdir SwiftWasmApp && cd SwiftWasmApp
-```
-
-3. Initialize the project from a template with `carton`:
-
-```
-carton init --template basic
-```
-
-4. Build the project and start the development server, `carton dev` can be kept running
- during development:
-
-```
-carton dev
-```
-
----
-
-
-
-Open [http://127.0.0.1:8080/](http://127.0.0.1:8080/) in your browser and a developer console
-within it. You'll see `Hello, world!` output in the console. You can edit the app source code in
-your favorite editor and save it, `carton` will immediately rebuild the app and reload all
-browser tabs that have the app open.
+Contributions are welcome! See [CONTRIBUTING.md](CONTRIBUTING.md) for details on how to contribute to the project.
## Sponsoring
[Become a gold or platinum sponsor](https://github.com/sponsors/swiftwasm/) and contact maintainers to add your logo on our README on Github with a link to your site.
-
diff --git a/Sources/JavaScriptKit/Documentation.docc/Articles/JavaScript-Environment-Requirements.md b/Sources/JavaScriptKit/Documentation.docc/Articles/JavaScript-Environment-Requirements.md
new file mode 100644
index 000000000..6483e4ca6
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Articles/JavaScript-Environment-Requirements.md
@@ -0,0 +1,48 @@
+# JavaScript Environment Requirements
+
+## Required JavaScript Features
+
+The JavaScript package produced by the JavaScriptKit packaging plugin requires the following JavaScript features:
+
+- [`FinalizationRegistry`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/FinalizationRegistry#browser_compatibility)
+- [WebAssembly BigInt to i64 conversion in JS API](https://caniuse.com/wasm-bigint)
+
+## Browser Compatibility
+
+These JavaScript features are supported in the following browsers:
+
+- Chrome 85+ (August 2020)
+- Firefox 79+ (July 2020)
+- Desktop Safari 14.1+ (April 2021)
+- Mobile Safari 14.5+ (April 2021)
+- Edge 85+ (August 2020)
+- Node.js 15.0+ (October 2020)
+
+Older browsers will not be able to run applications built with JavaScriptKit unless polyfills are provided.
+
+## Handling Missing Features
+
+### FinalizationRegistry
+
+When using JavaScriptKit in environments without `FinalizationRegistry` support, you can:
+
+1. Build with the opt-out flag: `-Xswiftc -DJAVASCRIPTKIT_WITHOUT_WEAKREFS`
+2. Then manually manage memory by calling `release()` on all `JSClosure` instances:
+
+```swift
+let closure = JSClosure { args in
+ // Your code here
+ return .undefined
+}
+
+// Use the closure...
+
+// Then release it when done
+#if JAVASCRIPTKIT_WITHOUT_WEAKREFS
+closure.release()
+#endif
+```
+
+### WebAssembly BigInt Support
+
+If you need to work with 64-bit integers in JavaScript, ensure your target environment supports WebAssembly BigInt conversions. For environments that don't support this feature, you'll need to avoid importing `JavaScriptBigIntSupport`
diff --git a/Sources/JavaScriptKit/Documentation.docc/Documentation.md b/Sources/JavaScriptKit/Documentation.docc/Documentation.md
new file mode 100644
index 000000000..94d5ba3c5
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Documentation.md
@@ -0,0 +1,56 @@
+# ``JavaScriptKit``
+
+Swift framework to interact with JavaScript through WebAssembly.
+
+## Overview
+
+**JavaScriptKit** provides a seamless way to interact with JavaScript from Swift code when compiled to WebAssembly.
+
+## Quick Start
+
+Check out the tutorial for a step-by-step guide to getting started.
+
+### Key Features
+
+- Access JavaScript objects and functions
+- Create closures that can be called from JavaScript
+- Convert between Swift and JavaScript data types
+- Use JavaScript promises with Swift's `async/await`
+- Work with multi-threading
+
+### Example
+
+```swift
+import JavaScriptKit
+
+// Access global JavaScript objects
+let document = JSObject.global.document
+
+// Create and manipulate DOM elements
+var div = document.createElement("div")
+div.innerText = "Hello from Swift!"
+_ = document.body.appendChild(div)
+
+// Handle events with Swift closures
+var button = document.createElement("button")
+button.innerText = "Click me"
+button.onclick = .object(JSClosure { _ in
+ JSObject.global.alert!("Button clicked!")
+ return .undefined
+})
+_ = document.body.appendChild(button)
+```
+
+Check out the [examples](https://github.com/swiftwasm/JavaScriptKit/tree/main/Examples) for more detailed usage.
+
+## Topics
+
+### Tutorials
+
+-
+
+### Core Types
+
+-
+-
+-
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial
new file mode 100644
index 000000000..f5ede8f19
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Hello-World.tutorial
@@ -0,0 +1,101 @@
+@Tutorial(time: 5) {
+ @Intro(title: "Quick Start: Hello World") {
+ This tutorial walks you through creating a simple web application using JavaScriptKit. You'll learn how to set up a Swift package, add JavaScriptKit as a dependency, write code to manipulate the DOM, and build and run your web application.
+
+ JavaScriptKit allows you to interact with JavaScript APIs directly from Swift code when targeting WebAssembly, making it easy to build web applications using Swift.
+ }
+
+ @Section(title: "Prerequisites") {
+ Visit the [installation guide](https://book.swiftwasm.org/getting-started/setup.html) to install the Swift SDK for WebAssembly before starting this tutorial.
+ This tutorial assumes you have the Swift SDK for WebAssembly installed. Please check your Swift installation.
+
+ @Steps {
+ @Step {
+ Check your Swift toolchain version. If you see different
+ @Code(name: "Console", file: "hello-world-0-1-swift-version.txt")
+ }
+ @Step {
+ Select a Swift SDK for WebAssembly version that matches the version of the Swift toolchain you have installed.
+
+ The following sections of this tutorial assume you have set the `SWIFT_SDK_ID` environment variable.
+ @Code(name: "Console", file: "hello-world-0-2-select-sdk.txt")
+ }
+ }
+ }
+
+ @Section(title: "Set up your project") {
+ Let's start by creating a new Swift package and configuring it to use JavaScriptKit.
+
+ @Steps {
+ @Step {
+ Create a new Swift package by running the following command in your terminal:
+ This creates a new Swift executable package named "Hello" with a basic folder structure.
+
+ @Code(name: "Console", file: "hello-world-1-1-init-package.txt")
+ }
+
+ @Step {
+ Add JavaScriptKit as a dependency using the Swift Package Manager:
+ This command adds the JavaScriptKit GitHub repository as a dependency to your package.
+
+ @Code(name: "Console", file: "hello-world-1-2-add-dependency.txt")
+ }
+
+ @Step {
+ Add JavaScriptKit as a target dependency:
+ This command adds JavaScriptKit as a target dependency to your package.
+
+ @Code(name: "Console", file: "hello-world-1-3-add-target-dependency.txt")
+ }
+ }
+ }
+
+ @Section(title: "Write your web application") {
+ Now let's write some Swift code that manipulates the DOM to create a simple web page.
+
+ @Steps {
+ @Step {
+ Create or modify the main.swift file in your Sources/Hello directory:
+ This code creates a new div element, sets its text content to "Hello from Swift!", and appends it to the document body.
+
+ @Code(name: "main.swift", file: "hello-world-2-1-main-swift.swift")
+ }
+
+ @Step {
+ Create an index.html file in the root of your project to load your WebAssembly application:
+ This HTML file includes a script that loads and runs your compiled WebAssembly code.
+
+ @Code(name: "index.html", file: "hello-world-2-2-index-html.html")
+ }
+ }
+ }
+
+ @Section(title: "Build and run your application") {
+ Let's build your application and run it in a web browser.
+
+ @Steps {
+ @Step {
+ Build your application with the Swift WebAssembly toolchain:
+ This command compiles your Swift code to WebAssembly and generates the necessary JavaScript bindings.
+
+ @Code(name: "Console", file: "hello-world-3-1-build.txt")
+ }
+
+ @Step {
+ Start a local web server to serve your application:
+ This starts a simple HTTP server that serves files from your current directory.
+
+ @Code(name: "Console", file: "hello-world-3-2-server.txt")
+ }
+
+ @Step {
+ Open your application in a web browser:
+ Your browser should open and display a page with "Hello from Swift!" as text added by your Swift code.
+
+ @Code(name: "Console", file: "hello-world-3-3-open.txt") {
+ @Image(alt: "Preview of the web application", source: "hello-world-3-3-app.png")
+ }
+ }
+ }
+ }
+}
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-0-1-swift-version.txt b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-0-1-swift-version.txt
new file mode 100644
index 000000000..5d5ad28df
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-0-1-swift-version.txt
@@ -0,0 +1,7 @@
+$ swift --version
+Apple Swift version 6.0.3 (swift-6.0.3-RELEASE)
+or
+Swift version 6.0.3 (swift-6.0.3-RELEASE)
+
+$ swift sdk list
+6.0.3-RELEASE-wasm32-unknown-wasi
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-0-2-select-sdk.txt b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-0-2-select-sdk.txt
new file mode 100644
index 000000000..b5fc2c620
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-0-2-select-sdk.txt
@@ -0,0 +1,9 @@
+$ swift --version
+Apple Swift version 6.0.3 (swift-6.0.3-RELEASE)
+or
+Swift version 6.0.3 (swift-6.0.3-RELEASE)
+
+$ swift sdk list
+6.0.3-RELEASE-wasm32-unknown-wasi
+
+$ export SWIFT_SDK_ID=6.0.3-RELEASE-wasm32-unknown-wasi
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-1-init-package.txt b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-1-init-package.txt
new file mode 100644
index 000000000..938b88e01
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-1-init-package.txt
@@ -0,0 +1,6 @@
+$ swift package init --name Hello --type executable
+Creating executable package: Hello
+Creating Package.swift
+Creating .gitignore
+Creating Sources/
+Creating Sources/main.swift
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-2-add-dependency.txt b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-2-add-dependency.txt
new file mode 100644
index 000000000..358629d0c
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-2-add-dependency.txt
@@ -0,0 +1,9 @@
+$ swift package init --name Hello --type executable
+Creating executable package: Hello
+Creating Package.swift
+Creating .gitignore
+Creating Sources/
+Creating Sources/main.swift
+
+$ swift package add-dependency https://github.com/swiftwasm/JavaScriptKit.git --branch main
+Updating package manifest at Package.swift... done.
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-3-add-target-dependency.txt b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-3-add-target-dependency.txt
new file mode 100644
index 000000000..317690412
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-1-3-add-target-dependency.txt
@@ -0,0 +1,12 @@
+$ swift package init --name Hello --type executable
+Creating executable package: Hello
+Creating Package.swift
+Creating .gitignore
+Creating Sources/
+Creating Sources/main.swift
+
+$ swift package add-dependency https://github.com/swiftwasm/JavaScriptKit.git --branch main
+Updating package manifest at Package.swift... done.
+
+$ swift package add-target-dependency --package JavaScriptKit JavaScriptKit Hello
+Updating package manifest at Package.swift... done.
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-1-main-swift.swift b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-1-main-swift.swift
new file mode 100644
index 000000000..156ac0540
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-1-main-swift.swift
@@ -0,0 +1,6 @@
+import JavaScriptKit
+
+let document = JSObject.global.document
+var div = document.createElement("div")
+div.innerText = "Hello from Swift!"
+document.body.appendChild(div)
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-2-index-html.html b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-2-index-html.html
new file mode 100644
index 000000000..84a3aa15e
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-2-2-index-html.html
@@ -0,0 +1,14 @@
+
+
+
+
+ Swift Web App
+
+
+
+ My Swift Web App
+
+
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-1-build.txt b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-1-build.txt
new file mode 100644
index 000000000..9c0ef39c2
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-1-build.txt
@@ -0,0 +1,6 @@
+$ swift package --swift-sdk $SWIFT_SDK_ID js --use-cdn
+[37/37] Linking Hello.wasm
+Build of product 'Hello' complete! (5.16s)
+Packaging...
+...
+Packaging finished
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-2-server.txt b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-2-server.txt
new file mode 100644
index 000000000..569396481
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-2-server.txt
@@ -0,0 +1,8 @@
+$ swift package --swift-sdk $SWIFT_SDK_ID js --use-cdn
+[37/37] Linking Hello.wasm
+Build of product 'Hello' complete! (5.16s)
+Packaging...
+...
+Packaging finished
+$ python3 -m http.server
+Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-app.png b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-app.png
new file mode 100644
index 000000000..033cafbcd
Binary files /dev/null and b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-app.png differ
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-open.txt b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-open.txt
new file mode 100644
index 000000000..f4df8ec2f
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Hello-World/Resources/hello-world-3-3-open.txt
@@ -0,0 +1,9 @@
+$ swift package --swift-sdk $SWIFT_SDK_ID js --use-cdn
+[37/37] Linking Hello.wasm
+Build of product 'Hello' complete! (5.16s)
+Packaging...
+...
+Packaging finished
+$ python3 -m http.server
+Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
+$ open http://localhost:8000
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Resources/image.png b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Resources/image.png
new file mode 100644
index 000000000..5b24016a9
Binary files /dev/null and b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Resources/image.png differ
diff --git a/Sources/JavaScriptKit/Documentation.docc/Tutorials/Table-of-Contents.tutorial b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Table-of-Contents.tutorial
new file mode 100644
index 000000000..c2950be1e
--- /dev/null
+++ b/Sources/JavaScriptKit/Documentation.docc/Tutorials/Table-of-Contents.tutorial
@@ -0,0 +1,9 @@
+@Tutorials(name: "JavaScriptKit") {
+ @Intro(title: "Working with JavaScriptKit") {
+ JavaScriptKit is a Swift package that allows you to interact with JavaScript APIs directly from Swift code when targeting WebAssembly.
+ }
+ @Chapter(name: "Hello World") {
+ @Image(source: "image.png")
+ @TutorialReference(tutorial: "doc:Hello-World")
+ }
+}