Skip to content
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
10 changes: 10 additions & 0 deletions .github/labeler.yml
Original file line number Diff line number Diff line change
Expand Up @@ -183,6 +183,16 @@ vscode:
- any-glob-to-any-file:
- '**/javascript/packages/vscode/**/*'

dev-client:
- changed-files:
- any-glob-to-any-file:
- '**/javascript/packages/client/**/*'

dev-server:
- changed-files:
- any-glob-to-any-file:
- 'lib/herb/dev/**/*'

dev-tools:
- changed-files:
- any-glob-to-any-file:
Expand Down
52 changes: 33 additions & 19 deletions .rubocop.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,10 @@ Style/SymbolArray:
Style/BlockDelimiters:
Enabled: false

Style/SafeNavigationChainLength:
Exclude:
- lib/herb/dev/runner.rb

Style/GlobalVars:
Exclude:
- ext/herb/extconf.rb
Expand All @@ -55,61 +59,68 @@ Style/TrailingCommaInHashLiteral:
Metrics/CyclomaticComplexity:
Max: 15
Exclude:
- lib/herb/project.rb
- bench/**/*
- lib/herb/ast/nodes.rb
- lib/herb/cli.rb
- lib/herb/dev/runner.rb
- lib/herb/engine.rb
- lib/herb/prism_inspect.rb
- lib/herb/engine/**/*.rb
- lib/herb/prism_inspect.rb
- lib/herb/project.rb
- templates/template.rb
- bench/**/*

Metrics/MethodLength:
Max: 20
Exclude:
- bench/**/*
- bin/**/*
- lib/herb/ast/nodes.rb
- lib/herb/cli.rb
- lib/herb/project.rb
- lib/herb/dev/runner.rb
- lib/herb/dev/server.rb
- lib/herb/engine.rb
- lib/herb/engine/**/*.rb
- lib/herb/prism_inspect.rb
- lib/herb/project.rb
- templates/template.rb
- test/fork_helper.rb
- test/snapshot_utils.rb
- bin/**/*
- bench/**/*

Metrics/AbcSize:
Exclude:
- bench/**/*
- bin/**/*
- lib/herb/ast/node.rb
- lib/herb/ast/nodes.rb
- lib/herb/cli.rb
- lib/herb/errors.rb
- lib/herb/project.rb
- lib/herb/dev/server.rb
- lib/herb/dev/runner.rb
- lib/herb/engine.rb
- lib/herb/engine/**/*.rb
- lib/herb/token.rb
- lib/herb/errors.rb
- lib/herb/prism_inspect.rb
- lib/herb/project.rb
- lib/herb/token.rb
- templates/template.rb
- test/engine/action_view/action_view_test_helper.rb
- test/engine/rails_compatibility_test.rb
- test/fork_helper.rb
- test/snapshot_utils.rb
- test/engine/rails_compatibility_test.rb
- bin/**/*
- bench/**/*
- test/engine/action_view/action_view_test_helper.rb

Metrics/ClassLength:
Exclude:
- bin/**/*
- lib/herb/ast/nodes.rb
- lib/herb/cli.rb
- lib/herb/configuration.rb
- lib/herb/dev/runner.rb
- lib/herb/dev/server.rb
- lib/herb/engine.rb
- lib/herb/engine/**/*.rb
- lib/herb/project.rb
- lib/herb/visitor.rb
- lib/herb/ast/nodes.rb
- test/**/*_test.rb
- templates/template.rb
- test/**/*_test.rb

Metrics/ModuleLength:
Exclude:
Expand All @@ -133,6 +144,7 @@ Metrics/ParameterLists:
Exclude:
- lib/herb/ast/node.rb
- lib/herb/ast/nodes.rb
- lib/herb/dev/runner.rb
- lib/herb/diff_operation.rb
- lib/herb/engine/validators/security_validator.rb
- lib/herb/errors.rb
Expand All @@ -141,16 +153,18 @@ Metrics/ParameterLists:

Metrics/PerceivedComplexity:
Exclude:
- bench/**/*
- bin/**/*
- lib/herb/ast/nodes.rb
- lib/herb/cli.rb
- lib/herb/project.rb
- lib/herb/prism_inspect.rb
- lib/herb/dev/runner.rb
- lib/herb/dev/server.rb
- lib/herb/engine.rb
- lib/herb/engine/**/*.rb
- lib/herb/prism_inspect.rb
- lib/herb/project.rb
- templates/template.rb
- test/**/*.rb
- bin/**/*
- bench/**/*

Layout/LineLength:
Enabled: false
Expand Down
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ gem "prism", github: "ruby/prism", tag: "v1.9.0"
gem "actionview", "~> 8.0"
gem "benchmark"
gem "charm"
gem "cruise"
gem "digest", "~> 3.2"
gem "erubi"
gem "irb", "~> 1.16"
Expand Down
7 changes: 7 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -114,6 +114,12 @@ GEM
concurrent-ruby (1.3.6)
connection_pool (3.0.2)
crass (1.0.6)
cruise (0.1.0-aarch64-linux-gnu)
cruise (0.1.0-aarch64-linux-musl)
cruise (0.1.0-arm64-darwin)
cruise (0.1.0-x86_64-darwin)
cruise (0.1.0-x86_64-linux-gnu)
cruise (0.1.0-x86_64-linux-musl)
csv (3.3.5)
date (3.5.1)
difftastic (0.8.0-aarch64-linux)
Expand Down Expand Up @@ -309,6 +315,7 @@ DEPENDENCIES
actionview (~> 8.0)
benchmark
charm
cruise
digest (~> 3.2)
erubi
herb!
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ The Herb ecosystem offers multiple tools that integrate seamlessly into editors,
| [Herb Language Service](https://herb-tools.dev/projects/language-service) | HTML+ERB language service with ActionView tag helper support. |
| [Herb Language Server](https://herb-tools.dev/projects/language-server) | Rich editor integration for VS Code, Zed, Neovim, and more. |
| [Herb Engine](https://herb-tools.dev/projects/engine) | HTML-aware ERB rendering engine, API-compatible with Erubi. |
| [Herb Dev Server](https://herb-tools.dev/projects/dev-server) | File-watching dev server with live DOM patching for templates. *(experimental)* |
| [Herb Dev Tools](https://herb-tools.dev/projects/dev-tools) | In-browser dev tools for inspecting and debugging templates, shipped with ReActionView. |
| [ReActionView](https://reactionview.dev) | ActionView-compatible ERB engine with modern DX for Rails. |

Expand Down
3 changes: 3 additions & 0 deletions Steepfile
Original file line number Diff line number Diff line change
Expand Up @@ -17,4 +17,7 @@ target :lib do
ignore "lib/herb/project.rb"
ignore "lib/herb/engine/error_formatter.rb"
ignore "lib/herb/bootstrap.rb"
ignore "lib/herb/dev/server.rb"
ignore "lib/herb/dev/server_entry.rb"
ignore "lib/herb/dev/runner.rb"
end
2 changes: 2 additions & 0 deletions docs/.vitepress/config/theme.mts
Original file line number Diff line number Diff line change
Expand Up @@ -18,13 +18,15 @@ const defaultSidebar = [
{ text: "Formatter", link: "/projects/formatter" },
{ text: "Linter", link: "/projects/linter" },
{ text: "CLI", link: "/projects/cli" },
{ text: "Dev Server", link: "/projects/dev-server" },
{ text: "Dev Tools", link: "/projects/dev-tools" },
],
},
{
text: "Utility Libraries",
collapsed: false,
items: [
{ text: "Dev Server Client", link: "/projects/client" },
{ text: "Language Service", link: "/projects/language-service" },
{ text: "Highlighter", link: "/projects/highlighter" },
{ text: "Syntax Tree Printer", link: "/projects/printer" },
Expand Down
2 changes: 2 additions & 0 deletions docs/docs/projects.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ These are the user-facing tools that integrate into your development workflow:
* [Herb Language Server](/projects/language-server)
* [Herb Formatter](/projects/formatter)
* [Herb Linter](/projects/linter)
* [Herb Dev Server](/projects/dev-server)
* [Herb CLI](/projects/cli)

## Language Bindings
Expand All @@ -24,6 +25,7 @@ These libraries provide access to the Herb Parser in different programming langu

These specialized libraries provide additional functionality for working with HTML+ERB:

* [Dev Server Client](/projects/client)
* [Language Service](/projects/language-service)
* [Highlighter](/projects/highlighter)
* [Syntax Tree Printer](/projects/minifier)
Expand Down
1 change: 1 addition & 0 deletions docs/docs/projects/client.md
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<!-- @include: ../../../javascript/packages/client/README.md -->
56 changes: 56 additions & 0 deletions docs/docs/projects/dev-server.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Herb Dev Server

The Herb Dev Server watches template files and provides real-time hot-reloading during development via WebSocket.

> [!WARNING]
> The dev server is experimental and may not work correctly in all cases.

## Usage

```bash
herb dev [directory] [--port 8592]
```

The server starts watching all template files in the directory, diffs changes using the Herb diff engine, and broadcasts updates to connected clients.

## How It Works

1. **File watching**: monitors all `.html.erb`, `.html.herb`, and other template files for changes
2. **AST diffing**: when a file changes, diffs the old and new AST to determine what changed
3. **Smart patching**: for text and attribute changes, sends a patch that the client applies without reloading
4. **Reload fallback**: for structural changes (insertions, removals, ERB changes), tells the client to reload

## Architecture

The dev server consists of two parts:

- **Server** (`lib/herb/dev/`): Ruby WebSocket server that watches files and broadcasts changes
- **Client** (`@herb-tools/client`): JavaScript package that connects to the server and applies DOM patches

## CLI Output

```
🌿 Herb Dev Server

⚠️ Experimental: The dev server is experimental and may not work correctly in all cases.

Herb: 0.9.7
Project: /path/to/project
Config: .herb.yml
Files: 453 templates indexed
WebSocket: ws://localhost:8592

Ready! Watching for changes...

Recent changes:

20:13:40 ✓ patch app/views/posts/show.html.erb (1 operation) [1 client]
#1 text changed [4, 8]
20:13:45 ↻ reload app/views/posts/index.html.erb (2 operations) [1 client]
#1 node inserted [0, 3]
#2 text changed [0, 4]
```

## Links

- [Dev Server Client (`@herb-tools/client`)](/projects/client)
45 changes: 45 additions & 0 deletions javascript/packages/client/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
# Herb Client

Dev server client for Herb templates, connects to the Herb Dev Server and applies live DOM patches during development.

> [!WARNING]
> The dev server and client are experimental and may not work correctly in all cases.

## How It Works

The client connects to the Herb Dev Server via WebSocket and receives messages when template files change. Depending on the type of change:

- **Text and attribute changes** are patched directly in the DOM without a page reload
- **Structural changes** (insertions, removals, ERB changes) trigger a full page reload

## Usage

The client auto-initializes when `<meta name="herb-debug-mode" content="true">` is present in the page.

> [!NOTE]
> No manual setup required when using ReActionView with `debug_mode` enabled.

### Manual initialization

```typescript
import { initHerbClient } from "@herb-tools/client"

const client = initHerbClient({
port: 8592,
host: "localhost",
onPatch: (message) => console.log("Patched:", message.file),
onReload: (message) => console.log("Reloading for:", message.file),
})
```

## Protocol

The client communicates with the Herb Dev Server using these message types:

| Message | Direction | Description |
|---------|-----------|-------------|
| `welcome` | Server → Client | Handshake with project path |
| `patch` | Server → Client | Text/attribute changes to apply |
| `reload` | Server → Client | Structural change requiring reload |
| `error` | Server → Client | Parse errors detected |
| `fixed` | Server → Client | Parse errors resolved |
39 changes: 39 additions & 0 deletions javascript/packages/client/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
{
"name": "@herb-tools/client",
"version": "0.9.7",
"description": "HMR client for Herb templates - connects to herb dev server and applies live DOM patches",
"type": "module",
"license": "MIT",
"homepage": "https://herb-tools.dev",
"bugs": "https://github.com/marcoroth/herb/issues/new?title=Package%20%60@herb-tools/client%60:%20",
"repository": {
"type": "git",
"url": "git+https://github.com/marcoroth/herb.git",
"directory": "javascript/packages/client"
},
"main": "./dist/herb-client.umd.js",
"module": "./dist/herb-client.esm.js",
"types": "./dist/types/index.d.ts",
"scripts": {
"build": "yarn clean && rollup -c",
"dev": "rollup -c -w",
"clean": "rimraf dist",
"test": "vitest run",
"prepublishOnly": "yarn clean && yarn build && yarn test"
},
"exports": {
"./package.json": "./package.json",
".": {
"types": "./dist/types/index.d.ts",
"import": "./dist/herb-client.esm.js",
"default": "./dist/herb-client.umd.js"
}
},
"dependencies": {},
"files": [
"package.json",
"README.md",
"dist/",
"src/"
]
}
27 changes: 27 additions & 0 deletions javascript/packages/client/project.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
{
"name": "@herb-tools/client",
"$schema": "../../../node_modules/nx/schemas/project-schema.json",
"sourceRoot": "javascript/packages/client/src",
"projectType": "library",
"targets": {
"build": {
"executor": "nx:run-script",
"options": {
"script": "build"
}
},
"test": {
"executor": "nx:run-script",
"options": {
"script": "test"
}
},
"clean": {
"executor": "nx:run-script",
"options": {
"script": "clean"
}
}
},
"tags": []
}
Loading
Loading