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
1 change: 1 addition & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
* text=auto eol=lf
2 changes: 1 addition & 1 deletion .github/workflows/playwright.yml
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,6 @@ jobs:
- name: Install dependencies
run: npm ci
- name: Install Playwright Browsers
run: npx playwright install --with-deps chrome webkit
run: npx playwright install --with-deps chromium webkit
- name: Run Playwright tests
run: npm run test:e2e
26 changes: 26 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,32 @@ const button = jsx`
document.body.append(button)
```

### Source transpilation (`transpileJsxSource`)

Need to transform raw JSX source text (e.g. code typed in an editor) without Babel? Use `transpileJsxSource`:

```ts
import { transpileJsxSource } from '@knighted/jsx/transpile'

const input = `
const App = () => {
return <button>click me</button>
}
`

const { code } = transpileJsxSource(input)
// -> const App = () => { return React.createElement("button", null, "click me") }
```

By default this emits `React.createElement(...)` and `React.Fragment`. Override them when needed:

```ts
transpileJsxSource(input, {
createElement: '__jsx',
fragment: '__fragment',
})
```

### React runtime (`reactJsx`)

Need to compose React elements instead of DOM nodes? Import the dedicated helper from the `@knighted/jsx/react` subpath (React 18+ and `react-dom` are still required to mount the tree):
Expand Down
12 changes: 6 additions & 6 deletions docs/how-it-compares.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,21 @@ Use this quick matrix to see how `@knighted/jsx` stacks up against other tagged-
| SSR / Node | `@knighted/jsx/node` bootstraps `linkedom`/`jsdom` automatically; fixtures cover Next.js, Lit + React hybrids, and plain Node usage. | Depends on the hyperscript target/framework to provide SSR. | Provides DOM-focused SSR utilities but no automatic shims or React interop. |
| TypeScript support | First-class typings for DOM + React runtimes, loader options, and Node helpers. | Community types only for the tag factory. | Minimal typings; templates rely on generic DOM types. |
| Component interoperability | Mix DOM helpers, React components, Lit roots, and loader-transformed calls in one file. | Primarily a JSX stand-in for Preact or hyperscript. | Focused on DOM updates, pairs with `uhtml/async` or low-level renderers. |
| Approx. size | DOM: ~11.3 kB raw / ~2.8 kB min+gzip. Lite DOM: ~10.1 kB raw / ~4.2 kB min+gzip. | ~1 kB min+gzip. | ~7 kB min+gzip. |
| Approx. size | DOM: ~11.5 kB raw / ~2.8 kB min+gzip. Lite DOM: ~10.4 kB raw / ~4.3 kB min+gzip. | ~1 kB min+gzip. | ~7 kB min+gzip. |

> `htm` and `uhtml` remain excellent when you only need lightweight hyperscript or DOM templating. `@knighted/jsx` trades a slightly larger runtime for full JSX semantics, React parity, loaders, and SSR tooling.

> [!NOTE]
> `@knighted/jsx` sizes were measured by gzipping `dist/jsx.js` (default runtime) and `dist/lite/index.js` from the latest build. The lite bundle packs the DOM helper and bootstrap code together, so raw bytes dropped while gzip remains ~1.4 kB larger than the split default entry.
> `@knighted/jsx` sizes were measured by gzipping `dist/jsx.js` (default runtime) and `dist/lite/index.js` from the latest build. The lite bundle packs the DOM helper and bootstrap code together, so raw bytes dropped while gzip remains ~1.5 kB larger than the split default entry.

## Detailed size breakdown

| Entry point | Raw size (bytes) | Min+gzip size (bytes) | Lite raw size (bytes) | Lite min+gzip size (bytes) | Notes |
| --------------------------------------------- | ----------------------------------------------------------------------------- | --------------------- | --------------------------------------- | -------------------------- | ---------------------------------------------------------------------------------------------------------------------- |
| DOM runtime (`@knighted/jsx`) | 11,549 (`dist/jsx.js`) | 2,870 | 10,337 (`dist/lite/index.js`) | 4,301 | Lite bundle keeps helper + bootstrap inline, shaving raw bytes (~10 kB) while staying ~1.4 kB heavier once gzipped. |
| React runtime (`@knighted/jsx/react`) | 5,236 (`dist/react/react-jsx.js`) | 1,478 | 6,777 (`dist/lite/react/index.js`) | 2,974 | Lite React remains a single file with helpers + bootstrap, so gzip lands roughly 1.5 kB above the split default build. |
| Node DOM entry (`@knighted/jsx/node`) | 14,044 combined (`dist/jsx.js` + `dist/node/bootstrap.js` via dynamic import) | ≈3,849 | 11,500 (`dist/lite/node/index.js`) | 4,808 | Lite node now reuses the trimmed DOM runtime, trimming ~1.3 kB raw compared to the previous monolithic build. |
| Node React entry (`@knighted/jsx/node/react`) | 7,731 combined (`dist/react/react-jsx.js` + `dist/node/bootstrap.js`) | ≈2,457 | 6,777 (`dist/lite/node/react/index.js`) | 2,974 | Lite node/react shares the lite React runtime, so gzip matches that entry while the classic build loads two files. |
| DOM runtime (`@knighted/jsx`) | 11,549 (`dist/jsx.js`) | 2,843 | 10,388 (`dist/lite/index.js`) | 4,295 | Lite bundle keeps helper + bootstrap inline, shaving raw bytes (~10 kB) while staying ~1.5 kB heavier once gzipped. |
| React runtime (`@knighted/jsx/react`) | 5,236 (`dist/react/react-jsx.js`) | 1,461 | 6,828 (`dist/lite/react/index.js`) | 2,979 | Lite React remains a single file with helpers + bootstrap, so gzip lands roughly 1.5 kB above the split default build. |
| Node DOM entry (`@knighted/jsx/node`) | 14,044 combined (`dist/jsx.js` + `dist/node/bootstrap.js` via dynamic import) | 3,803 | 11,551 (`dist/lite/node/index.js`) | 4,796 | Lite node now reuses the trimmed DOM runtime, trimming ~2.5 kB raw compared to the previous monolithic build. |
| Node React entry (`@knighted/jsx/node/react`) | 7,731 combined (`dist/react/react-jsx.js` + `dist/node/bootstrap.js`) | 2,421 | 6,828 (`dist/lite/node/react/index.js`) | 2,979 | Lite node/react shares the lite React runtime, so gzip matches that entry while the classic build loads two files. |

> [!TIP]
> Numbers were captured using `gzip -c <file> | wc -c`. “Combined” entries include every file the non-lite entry loads at runtime so you can compare end-to-end costs.
4 changes: 2 additions & 2 deletions examples/esm-demo.html
Original file line number Diff line number Diff line change
Expand Up @@ -512,7 +512,7 @@ <h1>@knighted/jsx + CDN</h1>
{
entry: '@knighted/jsx/lite',
context: 'DOM runtime helpers',
raw: '~10.3 kB raw',
raw: '~10.4 kB raw',
gzip: '~4.3 kB min+gzip',
note: 'Great for browser demos or DOM-like shims without bundlers.',
},
Expand All @@ -526,7 +526,7 @@ <h1>@knighted/jsx + CDN</h1>
{
entry: '@knighted/jsx/node/lite',
context: 'Node DOM entry',
raw: '~11.5 kB raw',
raw: '~11.6 kB raw',
gzip: '~4.8 kB min+gzip',
note: 'Bundles the DOM shim bootstrap for headless SSR scripts.',
},
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

7 changes: 6 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@knighted/jsx",
"version": "1.7.9",
"version": "1.8.0",
"description": "Runtime JSX tagged template that renders DOM or React trees anywhere with or without a build step.",
"keywords": [
"jsx runtime",
Expand Down Expand Up @@ -101,6 +101,11 @@
"import": "./dist/lite/node/react/index.js",
"default": "./dist/lite/node/react/index.js"
},
"./transpile": {
"types": "./dist/transpile.d.ts",
"import": "./dist/transpile.js",
"default": "./dist/transpile.js"
},
"./loader": {
"import": "./dist/loader/jsx.js",
"default": "./dist/loader/jsx.js"
Expand Down
4 changes: 2 additions & 2 deletions playwright.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ const LOADER_FIXTURE_PORT = Number(process.env.LOADER_FIXTURE_PORT ?? 4174)
const esmDemoUrl = process.env.ESM_DEMO_URL ?? `http://${HOST}:${ESM_DEMO_PORT}`
const projects: PlaywrightTestProject[] = [
{
name: 'chrome',
use: { channel: 'chrome' },
name: 'chromium',
use: { browserName: 'chromium' },
},
]

Expand Down
Loading
Loading