Skip to content
Merged
Show file tree
Hide file tree
Changes from 4 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
2 changes: 1 addition & 1 deletion .github/workflows/bundle-app.yml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ on:
app_path:
description: 'Path to app directory (relative to repo root)'
required: true
default: 'apps/example-deno-app'
default: 'examples/example-deno-app'
sign_windows:
description: 'Sign Windows MSIX package'
required: false
Expand Down
30 changes: 15 additions & 15 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ jobs:
cd forge-dist && tar -czvf ../forge-x86_64-pc-windows-msvc.tar.gz *

- name: Build example app
run: cargo run -p forge -- build apps/example-deno-app
run: cargo run -p forge -- build examples/example-deno-app

- name: Bundle example app (MSIX)
run: cargo run -p forge -- bundle apps/example-deno-app
run: cargo run -p forge -- bundle examples/example-deno-app

- name: Upload Windows artifacts
uses: actions/upload-artifact@v4
with:
name: windows-artifacts
path: |
forge-x86_64-pc-windows-msvc.tar.gz
apps/example-deno-app/bundle/*.msix
examples/example-deno-app/bundle/*.msix

build-macos-arm:
name: Build macOS (ARM)
Expand Down Expand Up @@ -106,19 +106,19 @@ jobs:
cd forge-dist && tar -czvf ../forge-aarch64-apple-darwin.tar.gz *

- name: Build example app
run: cargo run -p forge -- build apps/example-deno-app
run: cargo run -p forge -- build examples/example-deno-app

- name: Bundle example app (DMG)
run: cargo run -p forge -- bundle apps/example-deno-app
run: cargo run -p forge -- bundle examples/example-deno-app

- name: Upload macOS ARM artifacts
uses: actions/upload-artifact@v4
with:
name: macos-arm-artifacts
path: |
forge-aarch64-apple-darwin.tar.gz
apps/example-deno-app/bundle/*.dmg
apps/example-deno-app/bundle/*.app
examples/example-deno-app/bundle/*.dmg
examples/example-deno-app/bundle/*.app

build-linux:
name: Build Linux
Expand Down Expand Up @@ -173,19 +173,19 @@ jobs:
cd forge-dist && tar -czvf ../forge-x86_64-unknown-linux-gnu.tar.gz *

- name: Build example app
run: cargo run -p forge -- build apps/example-deno-app
run: cargo run -p forge -- build examples/example-deno-app

- name: Bundle example app (AppImage)
run: cargo run -p forge -- bundle apps/example-deno-app
run: cargo run -p forge -- bundle examples/example-deno-app

- name: Upload Linux artifacts
uses: actions/upload-artifact@v4
with:
name: linux-artifacts
path: |
forge-x86_64-unknown-linux-gnu.tar.gz
apps/example-deno-app/bundle/*.AppImage
apps/example-deno-app/bundle/*.tar.gz
examples/example-deno-app/bundle/*.AppImage
examples/example-deno-app/bundle/*.tar.gz

release:
name: Create Release
Expand Down Expand Up @@ -220,10 +220,10 @@ jobs:
cp artifacts/macos-arm/forge-aarch64-apple-darwin.tar.gz release/ || true
cp artifacts/linux/forge-x86_64-unknown-linux-gnu.tar.gz release/ || true
# Example app bundles
cp artifacts/windows/apps/example-deno-app/bundle/*.msix release/ || true
cp artifacts/macos-arm/apps/example-deno-app/bundle/*.dmg release/ || true
cp artifacts/linux/apps/example-deno-app/bundle/*.AppImage release/ || true
cp artifacts/linux/apps/example-deno-app/bundle/*.tar.gz release/ || true
cp artifacts/windows/examples/example-deno-app/bundle/*.msix release/ || true
cp artifacts/macos-arm/examples/example-deno-app/bundle/*.dmg release/ || true
cp artifacts/linux/examples/example-deno-app/bundle/*.AppImage release/ || true
cp artifacts/linux/examples/example-deno-app/bundle/*.tar.gz release/ || true

- name: Get tag name
id: tag
Expand Down
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -50,4 +50,5 @@ temp/
test-app-tmp/

# Site build
site/node_modules
site/node_modules
resources/
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Added

#### M0 - Bootstrap

- Initial Forge CLI with `init`, `dev`, `build`, and `bundle` commands
- Deno runtime embedding via `deno_core`
- Basic window creation with tao/wry
- `host:*` module loader for native capability access
- Static HTML serving in development mode

#### M1 - UI & Bridge

- `host:ui` module with window management (open, close, send, events)
- Preload script for renderer-side `window.host` API
- `app://` custom protocol handler for asset loading
Expand All @@ -28,6 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- React, Vue, Svelte, and Minimal templates

#### M2 - Core Operations

- `host:fs` module with 14 file system operations
- Read/write text and binary files
- Directory operations (readDir, mkdir, remove)
Expand All @@ -50,6 +53,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Structured error codes (1000-4000 range)

#### M3 - Packaging

- `forge build` command for bundling Deno code
- `forge bundle` command for platform-specific packages
- macOS: .app bundle with DMG creation
Expand All @@ -59,13 +63,15 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- Build-time asset embedding

#### M4 - Framework Transforms & HMR

- Vue SFC transform support via `@vue/compiler-sfc`
- Svelte component transform support
- Development server with hot module replacement (HMR)
- WebSocket-based live reload on port 35729
- File watcher integration for automatic rebuilds

#### M5 - Polishing & Documentation

- Example applications demonstrating key features:
- Todo App (React) - File persistence, menus, IPC
- Weather App (Vue) - HTTP fetch, notifications, tray icons
Expand Down Expand Up @@ -96,6 +102,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
## [Unreleased]

### Planned

- CI/CD workflows for automated testing and releases
- Additional platform-specific features
- Plugin system for extensibility
Expand Down
12 changes: 6 additions & 6 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ Forge is an Electron-like desktop application framework using Rust + Deno. It em
## User Commands (after installation)

```bash
# Scaffold a new app
forge init my-new-app
# Copy an example to start a new app
cp -r examples/react-app my-new-app

# Run app in dev mode
forge dev my-app
Expand Down Expand Up @@ -41,7 +41,7 @@ cargo build -p forge
cargo build -p forge-host

# Run sample app via cargo (development)
cargo run -p forge -- dev apps/example-deno-app
cargo run -p forge -- dev examples/example-deno-app

# Run tests
cargo test
Expand All @@ -52,7 +52,7 @@ cargo test
### Crate Structure

- **`crates/forge-host`**: Main runtime binary. Embeds Deno JsRuntime, creates windows via tao/wry, handles IPC between Deno and WebView renderers. Contains `build.rs` for asset embedding.
- **`crates/forge`**: CLI tool (`forge init/dev/build/bundle`). Scaffolds apps and orchestrates forge-host.
- **`crates/forge`**: CLI tool (`forge dev/build/bundle`). Orchestrates forge-host for app development.
- **`crates/ext_fs`**: Rust extension providing `host:fs` module (file operations exposed to Deno).
- **`crates/ext_ui`**: Rust extension providing `host:ui` module (window management, IPC bridge).

Expand Down Expand Up @@ -91,7 +91,7 @@ These resolve to ESM shims in `crates/ext_*/js/*.js` that call Rust ops via `Den
## App Structure

```text
apps/example-deno-app/
examples/example-deno-app/
├── manifest.app.toml # App metadata, window config, permissions
├── deno.json # Deno config
├── src/main.ts # Deno entry point (calls host:ui, host:fs)
Expand All @@ -110,7 +110,7 @@ apps/example-deno-app/

Implemented features:
- Window creation and `app://` loading
- `forge init` with templates (minimal, react, vue, svelte)
- Example apps for React, Next.js-style, Svelte, and vanilla TypeScript
- `forge dev` for development mode
- `forge build` for web asset bundling (esbuild via Deno)
- `forge bundle` for platform packaging (macOS .app/.dmg, Windows .msix, Linux AppImage)
Expand Down
5 changes: 5 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
[workspace]
members = [
"crates/forge-host",
"crates/forge-weld",
"crates/forge-weld-macro",
"crates/ext_fs",
"crates/ext_ui",
"crates/ext_net",
"crates/ext_sys",
"crates/ext_process",
"crates/ext_wasm",
"crates/ext_ipc",
"crates/ext_window",
"crates/forge"
]
resolver = "2"
28 changes: 18 additions & 10 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
# Forge

> Build cross-platform desktop apps with TypeScript/JavaScript and native capabilities.
> Build cross-platform desktop apps with TypeScript and native capabilities as well as wasm, bundle and weld support.

Forge is an Electron-like desktop application framework using **Rust** and **Deno**. Apps are 100% TypeScript/JavaScript - no per-app Rust required. The runtime provides native system access through a secure, capability-based API.
Forge is an Electron-like desktop application framework using **Rust** and **Deno**. Apps are 100% TypeScript - no per-app Rust required. The runtime provides native system access through a secure, capability-based API.

**Status:** Alpha (0.1.0-alpha.1)

Expand Down Expand Up @@ -48,8 +48,8 @@ cargo install --path crates/forge-host
## Quick Start

```bash
# Create a new app
forge init my-app
# Copy an example to start a new app
cp -r examples/react-app my-app
cd my-app

# Run in development mode
Expand Down Expand Up @@ -96,6 +96,7 @@ my-app/
## Example

**src/main.ts:**

```typescript
import { openWindow, windowEvents } from "host:ui";

Expand All @@ -112,6 +113,7 @@ for await (const event of windowEvents()) {
```

**web/index.html:**

```html
<script>
window.host.send("hello", { message: "Hi!" });
Expand All @@ -125,16 +127,20 @@ for await (const event of windowEvents()) {
- [Getting Started](docs/getting-started.md)
- [API Reference](docs/api/)
- [Architecture](docs/architecture.md)
- [Examples](apps/)
- [Examples](examples/)

## Example Apps

| App | Demonstrates |
|-----|--------------|
| [todo-app](apps/todo-app) | File persistence, menus, IPC (React) |
| [weather-app](apps/weather-app) | HTTP fetch, notifications, tray (Vue) |
| [text-editor](apps/text-editor) | Dialogs, context menus, file watching |
| [system-monitor](apps/system-monitor) | System info, multi-window |
| [example-deno-app](examples/example-deno-app) | Minimal starter app |
| [react-app](examples/react-app) | React with TypeScript and IPC |
| [nextjs-app](examples/nextjs-app) | Next.js-style routing patterns |
| [svelte-app](examples/svelte-app) | Svelte with TypeScript |
| [todo-app](examples/todo-app) | File persistence, menus, IPC |
| [text-editor](examples/text-editor) | Dialogs, context menus, file watching |
| [weather-app](examples/weather-app) | HTTP fetch, notifications, tray |
| [system-monitor](examples/system-monitor) | System info, multi-window |

## Crate Structure

Expand All @@ -161,7 +167,7 @@ cargo build --workspace
cargo test --workspace

# Run the example app
cargo run -p forge -- dev apps/example-deno-app
cargo run -p forge -- dev examples/example-deno-app

# Build with release optimizations
cargo build --workspace --release
Expand All @@ -170,9 +176,11 @@ cargo build --workspace --release
## Requirements

**For App Developers:**

- Deno 1.40+

**For Forge Contributors:**

- Rust 1.70+
- Deno 1.40+

Expand Down
3 changes: 3 additions & 0 deletions crates/ext_fs/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,6 @@ serde_json = "1"
notify = { version = "6.0", default-features = false, features = ["macos_kqueue"] }
globset = "0.4"
tracing = "0.1"

[build-dependencies]
forge-weld = { path = "../forge-weld" }
58 changes: 21 additions & 37 deletions crates/ext_fs/build.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,24 @@
use deno_ast::{EmitOptions, MediaType, ParseParams, TranspileModuleOptions, TranspileOptions};
use std::fs;
use std::path::Path;

/// Transpile TypeScript to JavaScript using deno_ast
fn transpile_ts(ts_code: &str, specifier: &str) -> String {
let parsed = deno_ast::parse_module(ParseParams {
specifier: deno_ast::ModuleSpecifier::parse(specifier).unwrap(),
text: ts_code.into(),
media_type: MediaType::TypeScript,
capture_tokens: false,
scope_analysis: false,
maybe_syntax: None,
})
.expect("Failed to parse TypeScript");

let transpile_result = parsed
.transpile(
&TranspileOptions::default(),
&TranspileModuleOptions::default(),
&EmitOptions::default(),
)
.expect("Failed to transpile TypeScript");

transpile_result.into_source().text
}
use forge_weld::ExtensionBuilder;

fn main() {
// Transpile js/init.ts to js/init.js
println!("cargo:rerun-if-changed=js/init.ts");

let ts_path = Path::new("js/init.ts");
let js_path = Path::new("js/init.js");

if ts_path.exists() {
let ts_code = fs::read_to_string(ts_path).expect("Failed to read js/init.ts");
let js_code = transpile_ts(&ts_code, "file:///init.ts");
fs::write(js_path, js_code).expect("Failed to write js/init.js");
}
ExtensionBuilder::new("host_fs", "host:fs")
.ts_path("ts/init.ts")
.ops(&[
"op_fs_read_text",
"op_fs_write_text",
"op_fs_read_bytes",
"op_fs_write_bytes",
"op_fs_stat",
"op_fs_read_dir",
"op_fs_mkdir",
"op_fs_remove",
"op_fs_rename",
"op_fs_copy",
"op_fs_exists",
"op_fs_watch",
"op_fs_watch_next",
"op_fs_watch_close",
])
.build()
.expect("Failed to build host_fs extension");
}
Loading
Loading