Skip to content

Commit a54a02b

Browse files
committed
refactored to packages
1 parent f8bc8e3 commit a54a02b

File tree

35 files changed

+173
-780
lines changed

35 files changed

+173
-780
lines changed

CLAUDE.md

Lines changed: 27 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,13 @@ deno run --allow-net --allow-read examples/todos/server.ts
1919

2020
## Architecture
2121

22-
**JSX Transform Pipeline:**
22+
HSX is a monorepo with two packages:
23+
24+
**Packages:**
25+
- `@srdjan/hsx` (packages/hsx/) - Core JSX renderer, `render()`, `renderHtml()`, `route()`, `id()`, `Fragment`, `hsxComponent()`, `hsxPage()`
26+
- `@srdjan/hsx-styles` (packages/hsx-styles/) - `hsxStyles`, `hsxStylesDark`, `HSX_STYLES_PATH`
27+
28+
**JSX Transform Pipeline (packages/hsx/):**
2329
1. `jsx-runtime.ts` - Minimal JSX runtime producing VNode tree
2430
2. `render.ts` - Walks VNode tree, normalizes HSX props per element type, auto-injects HTMX script before `</body>` when needed
2531
3. `hsx-normalize.ts` - Maps HSX attributes → `hx-*` attributes, tracks HTMX usage via RenderContext
@@ -36,12 +42,15 @@ deno run --allow-net --allow-read examples/todos/server.ts
3642

3743
**Safety:** `script`/`style` children are emitted verbatim (no escaping); never pass user input there.
3844

39-
**Entry Points (Modular):**
40-
- `index.ts` (`.`) - Everything: `render`, `renderHtml`, `route`, `id`, `Fragment`, `hsxComponent`, `hsxPage`
41-
- `core.ts` (`./core`) - Core rendering: `render`, `renderHtml`, `route`, `id`, `Fragment`, types
42-
- `component-model.ts` (`./component-model`) - Higher-level: `hsxComponent`, `hsxPage`
43-
- `styles.ts` (`./styles`) - Optional CSS: `hsxStyles`, `hsxStylesDark`, `HSX_STYLES_PATH`
44-
- `jsx-runtime.ts` (`./jsx-runtime`) - JSX compiler requirement (not for direct import)
45+
**Imports (Workspace-aware):**
46+
```ts
47+
import { render, id, route, Fragment, hsxComponent, hsxPage } from "@srdjan/hsx";
48+
import { hsxStyles, hsxStylesDark, HSX_STYLES_PATH } from "@srdjan/hsx-styles";
49+
```
50+
51+
**Legacy exports** are maintained for backward compatibility via root deno.json:
52+
- `./core``packages/hsx/mod.ts`
53+
- `./styles``packages/hsx-styles/mod.ts`
4554

4655
## Key Patterns
4756

@@ -85,6 +94,14 @@ const ids = { list: id("todo-list") }; // Type: Id<"todo-list"> = "#todo-list"
8594

8695
## JSX Configuration
8796

88-
Files using JSX need either:
89-
- Pragma: `/** @jsxImportSource ./src */` at file top
90-
- Or rely on `deno.json` compilerOptions (already configured)
97+
Files using JSX rely on root `deno.json` compilerOptions (already configured):
98+
```json
99+
{
100+
"compilerOptions": {
101+
"jsx": "react-jsx",
102+
"jsxImportSource": "hsx"
103+
}
104+
}
105+
```
106+
107+
The workspace `imports` map resolves `hsx/jsx-runtime` to `packages/hsx/jsx-runtime.ts`.

README.md

Lines changed: 66 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
# HSX
22

3-
SSR-only JSX/TSX renderer for Deno that compiles HTMX-style attributes to `hx-*`
4-
on the server.
3+
First things, first... What the hack does HSX stand for? I'll say it's '**H**TML
4+
for **S**erver-Side e**X**tensions'. :0
5+
6+
But, silently, I prefer: **H**TML **S**laps **X**tremely :)
7+
8+
SSR-only JSX/TSX renderer for Deno that hides HTMX-style attributes away during the rendering process, and compiles them to `hx-*` attributes.
59

610
> Disclaimer: this was a quick hack in my free time, held together by vibe
7-
> coding and espresso. I like it a lot, but consider it an early release.
11+
> coding and espresso. I like it a lot, but consider it an early release. I feel it is getting better (a lot)
812
913
## TL;DR: : Like JSX, but for SSR HTML + HTMX.
1014

@@ -35,30 +39,40 @@ Or import directly:
3539
import { id, render, route } from "jsr:@srdjan/hsx";
3640
```
3741

38-
### Selective Imports (Tree-Shaking)
42+
### Separate Packages
3943

40-
HSX supports modular imports for smaller bundles:
44+
HSX is a monorepo with two packages:
4145

4246
```ts
43-
// Core only - rendering + type-safe routes (excludes hsxComponent/hsxPage)
44-
import { render, route, id, Fragment } from "jsr:@srdjan/hsx/core";
47+
// Core - JSX rendering, type-safe routes, hsxComponent, hsxPage
48+
import { Fragment, hsxComponent, hsxPage, id, render, route } from "jsr:@srdjan/hsx";
4549

46-
// Component model only - higher-level abstractions
47-
import { hsxComponent, hsxPage } from "jsr:@srdjan/hsx/component-model";
50+
// Styles - ready-to-use CSS with theming support
51+
import { HSX_STYLES_PATH, hsxStyles } from "jsr:@srdjan/hsx-styles";
52+
```
4853

49-
// Everything (default)
50-
import { render, route, hsxComponent, hsxPage } from "jsr:@srdjan/hsx";
54+
Install individually:
5155

52-
// Optional styles - ready-to-use CSS with theming support
53-
import { hsxStyles, HSX_STYLES_PATH } from "jsr:@srdjan/hsx/styles";
56+
```bash
57+
deno add jsr:@srdjan/hsx
58+
deno add jsr:@srdjan/hsx-styles
59+
```
60+
61+
### Legacy Exports (Backward Compatible)
62+
63+
For backward compatibility, subpath exports still work:
64+
65+
```ts
66+
import { Fragment, id, render, route } from "jsr:@srdjan/hsx/core";
67+
import { HSX_STYLES_PATH, hsxStyles } from "jsr:@srdjan/hsx/styles";
5468
```
5569

5670
### From Source
5771

58-
Clone and import:
72+
Clone and import using workspace package names:
5973

6074
```ts
61-
import { id, render, route } from "./src/index.ts";
75+
import { hsxComponent, hsxPage, id, render, route } from "@srdjan/hsx";
6276
```
6377

6478
## Quick Start (Low-Level API)
@@ -67,8 +81,7 @@ import { id, render, route } from "./src/index.ts";
6781
> the [hsxComponent pattern](#hsx-component-pattern-recommended) below instead.
6882
6983
```tsx
70-
/** @jsxImportSource ./src */
71-
import { id, render, route } from "./src/index.ts";
84+
import { id, render, route } from "@srdjan/hsx";
7285

7386
const routes = {
7487
todos: route("/todos", () => "/todos"),
@@ -346,7 +359,11 @@ if (url.pathname === "/static/htmx.js") {
346359
HSX includes an optional CSS module with a default theme and dark variant:
347360

348361
```ts
349-
import { hsxStyles, hsxStylesDark, HSX_STYLES_PATH } from "jsr:@srdjan/hsx/styles";
362+
import {
363+
HSX_STYLES_PATH,
364+
hsxStyles,
365+
hsxStylesDark,
366+
} from "jsr:@srdjan/hsx-styles";
350367

351368
// Serve the styles
352369
if (url.pathname === HSX_STYLES_PATH) {
@@ -356,23 +373,24 @@ if (url.pathname === HSX_STYLES_PATH) {
356373
}
357374

358375
// In your page head
359-
<link rel="stylesheet" href={HSX_STYLES_PATH} />
376+
<link rel="stylesheet" href={HSX_STYLES_PATH} />;
360377
```
361378

362379
**Exports:**
380+
363381
- `hsxStyles` - Default light theme (indigo accent)
364382
- `hsxStylesDark` - Dark theme variant
365383
- `HSX_STYLES_PATH` - Default path: `/static/hsx.css`
366384

367385
**Customization:** Override CSS variables in your page:
368386

369387
```tsx
370-
<style>{`:root { --hsx-accent: #10b981; --hsx-bg: #f0fdf4; }`}</style>
388+
<style>{`:root { --hsx-accent: #10b981; --hsx-bg: #f0fdf4; }`}</style>;
371389
```
372390

373-
Available variables: `--hsx-accent`, `--hsx-bg`, `--hsx-surface`, `--hsx-border`,
374-
`--hsx-text`, `--hsx-muted`, `--hsx-error`, `--hsx-success`, spacing (`--hsx-space-*`),
375-
and radius (`--hsx-radius-*`).
391+
Available variables: `--hsx-accent`, `--hsx-bg`, `--hsx-surface`,
392+
`--hsx-border`, `--hsx-text`, `--hsx-muted`, `--hsx-error`, `--hsx-success`,
393+
spacing (`--hsx-space-*`), and radius (`--hsx-radius-*`).
376394

377395
## API Reference
378396

@@ -483,34 +501,34 @@ Run examples with `deno task`:
483501
## Project Structure
484502

485503
```
486-
src/
487-
index.ts # Main entry - exports everything
488-
core.ts # Core module - render, route, id, Fragment, types
489-
component-model.ts # Component module - hsxComponent, hsxPage
490-
styles.ts # Optional CSS module with themes
491-
jsx-runtime.ts # Minimal JSX runtime (compiler requirement)
492-
render.ts # SSR renderer with HTMX injection
493-
hsx-normalize.ts # HSX to hx-* attribute mapping
494-
hsx-component.ts # hsxComponent factory (route + handler + render)
495-
hsx-page.ts # hsxPage guardrail for full-page layouts
496-
hsx-types.ts # Route, Id, HsxSwap, HsxTrigger types
497-
hsx-jsx.d.ts # JSX type declarations
504+
packages/
505+
hsx/ # Core package (@srdjan/hsx)
506+
mod.ts # Main entry point
507+
jsx-runtime.ts # Minimal JSX runtime (compiler requirement)
508+
render.ts # SSR renderer with HTMX injection
509+
hsx-normalize.ts # HSX to hx-* attribute mapping
510+
hsx-types.ts # Route, Id, HsxSwap, HsxTrigger types
511+
hsx-jsx.d.ts # JSX type declarations
512+
hsx-component.ts # hsxComponent factory (route + handler + render)
513+
hsx-page.ts # hsxPage guardrail for full-page layouts
514+
hsx-styles/ # Styles package (@srdjan/hsx-styles)
515+
mod.ts # Main entry point (CSS themes)
498516
examples/
499-
todos/ # Full todo app example
500-
active-search/ # Search example
501-
lazy-loading/ # Lazy load example
502-
form-validation/ # Form validation example
503-
polling/ # Polling example
504-
tabs-modal/ # Tabs and modal example
505-
hsx-components/ # HSX Component pattern example
506-
hsx-page/ # hsxPage full-page guardrail example
507-
low-level-api/ # Manual render/renderHtml without hsxPage/hsxComponent
517+
todos/ # Full todo app example
518+
active-search/ # Search example
519+
lazy-loading/ # Lazy load example
520+
form-validation/ # Form validation example
521+
polling/ # Polling example
522+
tabs-modal/ # Tabs and modal example
523+
hsx-components/ # HSX Component pattern example
524+
hsx-page/ # hsxPage full-page guardrail example
525+
low-level-api/ # Manual render/renderHtml without hsxPage/hsxComponent
508526
vendor/htmx/
509-
htmx.js # Vendored HTMX v4 (alpha)
527+
htmx.js # Vendored HTMX v4 (alpha)
510528
docs/
511-
USER_GUIDE.md # Comprehensive user guide
512-
HSX_OVERVIEW.md # Architecture overview
513-
HTMX_INTEGRATION.md # HTMX integration details
529+
USER_GUIDE.md # Comprehensive user guide
530+
HSX_OVERVIEW.md # Architecture overview
531+
HTMX_INTEGRATION.md # HTMX integration details
514532
```
515533

516534
## License

deno.json

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,20 @@
11
{
2-
"name": "@srdjan/hsx",
3-
"version": "0.6.0",
2+
"workspace": [
3+
"./packages/hsx",
4+
"./packages/hsx-styles"
5+
],
46
"exports": {
5-
".": "./src/index.ts",
6-
"./jsx-runtime": "./src/jsx-runtime.ts",
7-
"./core": "./src/core.ts",
8-
"./component-model": "./src/component-model.ts",
9-
"./styles": "./src/styles.ts"
7+
".": "./packages/hsx/mod.ts",
8+
"./jsx-runtime": "./packages/hsx/jsx-runtime.ts",
9+
"./core": "./packages/hsx/mod.ts",
10+
"./component-model": "./packages/hsx/mod.ts",
11+
"./styles": "./packages/hsx-styles/mod.ts"
1012
},
1113
"license": "MIT",
1214
"imports": {
13-
"hsx/jsx-runtime": "./src/jsx-runtime.ts"
15+
"@srdjan/hsx": "./packages/hsx/mod.ts",
16+
"@srdjan/hsx-styles": "./packages/hsx-styles/mod.ts",
17+
"hsx/jsx-runtime": "./packages/hsx/jsx-runtime.ts"
1418
},
1519
"compilerOptions": {
1620
"jsx": "react-jsx",
@@ -19,8 +23,8 @@
1923
"strict": true
2024
},
2125
"tasks": {
22-
"test": "deno test src/ --allow-read",
23-
"check": "deno check src/index.ts",
26+
"test": "deno test packages/ --allow-read",
27+
"check": "deno check packages/hsx/mod.ts packages/hsx-styles/mod.ts",
2428
"example:todos": "deno run --allow-net --allow-read examples/todos/server.tsx",
2529
"example:active-search": "deno run --allow-net --allow-read examples/active-search/server.tsx",
2630
"example:lazy-loading": "deno run --allow-net --allow-read examples/lazy-loading/server.tsx",

examples/active-search/ids.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
*
44
* Demonstrates type-safe element IDs for HTMX targeting.
55
*/
6-
import { id } from "../../src/hsx-types.ts";
6+
import { id } from "@srdjan/hsx";
77

88
export const ids = {
99
results: id("search-results"),

examples/active-search/server.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,9 @@
99
* - `vals` for passing additional data
1010
* - CSS indicator class for loading states
1111
*/
12-
import { hsxComponent, hsxPage, id } from "../../src/index.ts";
13-
import { hsxStyles, HSX_STYLES_PATH } from "../../src/styles.ts";
12+
import { hsxComponent, hsxPage } from "@srdjan/hsx";
13+
import { id } from "@srdjan/hsx";
14+
import { hsxStyles, HSX_STYLES_PATH } from "@srdjan/hsx-styles";
1415

1516
// =============================================================================
1617
// Sample Data

examples/form-validation/components.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Shared UI wrapper components for cleaner JSX
33
*/
4-
import type { HsxSwap, Urlish } from "../../src/hsx-types.ts";
4+
import type { HsxSwap, Urlish } from "@srdjan/hsx";
55

66
export function Subtitle(props: { children: string }) {
77
return (

examples/form-validation/ids.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/**
22
* Form Validation Example - Branded IDs
33
*/
4-
import { id } from "../../src/hsx-types.ts";
4+
import { id } from "@srdjan/hsx";
55

66
export const ids = {
77
usernameError: id("username-error"),

examples/form-validation/server.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
import { hsxComponent, hsxPage } from "../../src/index.ts";
2-
import { hsxStyles, HSX_STYLES_PATH } from "../../src/styles.ts";
1+
import { hsxComponent, hsxPage } from "@srdjan/hsx";
2+
import { hsxStyles, HSX_STYLES_PATH } from "@srdjan/hsx-styles";
33
import { Card, Subtitle } from "./components.tsx";
44
import { ids } from "./ids.ts";
55

examples/hsx-components/server.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,9 @@
55
* component are co-located in a single definition with type-safe
66
* enforcement between handler output and component props.
77
*/
8-
import { hsxComponent, hsxPage, id } from "../../src/index.ts";
9-
import { hsxStyles, HSX_STYLES_PATH } from "../../src/styles.ts";
8+
import { hsxComponent, hsxPage } from "@srdjan/hsx";
9+
import { id } from "@srdjan/hsx";
10+
import { hsxStyles, HSX_STYLES_PATH } from "@srdjan/hsx-styles";
1011

1112
// =============================================================================
1213
// Types & Data

examples/hsx-page/server.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
1-
import { hsxComponent, hsxPage, id, render } from "../../src/index.ts";
2-
import { hsxStylesDark, HSX_STYLES_PATH } from "../../src/styles.ts";
1+
import { hsxComponent, hsxPage } from "@srdjan/hsx";
2+
import { id } from "@srdjan/hsx";
3+
import { hsxStylesDark, HSX_STYLES_PATH } from "@srdjan/hsx-styles";
34

45
// ---------------------------------------------------------------------------
56
// Data (in-memory for demo)

0 commit comments

Comments
 (0)