Skip to content
Draft
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
5 changes: 5 additions & 0 deletions .changeset/add-tsrx-semantic-ir.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/compiler": patch
---

Add a typed semantic IR stage and a compiler-owned TSRX typecheck projection with authored source maps, style metadata, and embedded CSS/script regions.
5 changes: 5 additions & 0 deletions .changeset/fix-dom-insert-marker-anchors.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/compiler": patch
---

Fix DOM insert markers to reference the following sibling's declared walk variable (`_$insert(_el$, expr, _el$2)`) instead of re-deriving the walk inline (`_el$.firstChild`), matching babel-plugin output. Affects dynamic slots followed by static content in both single-slot and per-slot parents.
5 changes: 5 additions & 0 deletions .changeset/fix-tsrx-dynamic-child-style-scoping.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/compiler": patch
---

Preserve scoped selectors when a TSRX element contains dynamic children.
6 changes: 6 additions & 0 deletions .changeset/fix-tsrx-index-loop-mode.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@solidjs/babel-plugin": patch
"@solidjs/compiler": patch
---

Compile TSRX loops with an index but no explicit key using Solid's non-keyed callback shape.
5 changes: 5 additions & 0 deletions .changeset/fix-tsrx-lazy-update-rewrites.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/compiler": patch
---

Rewrite prefix and postfix updates of TSRX lazy bindings to their deferred member targets, matching the Babel frontend.
6 changes: 6 additions & 0 deletions .changeset/improve-tsrx-lazy-destructuring.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@solidjs/babel-plugin": minor
"@solidjs/compiler": minor
---

Support recursive lazy destructuring, lazy arrow parameters, per-read defaults, computed keys, rest views, standalone assignments, accessor-backed keyed-loop and catch patterns, and JavaScript-correct writes and updates across the Babel and native TSRX frontends.
5 changes: 5 additions & 0 deletions .changeset/support-tsrx-expression-containers.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/compiler": patch
---

Support TSRX statement containers in expression positions in the native compiler.
6 changes: 6 additions & 0 deletions .changeset/support-tsrx-scoped-styles.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@solidjs/babel-plugin": patch
"@solidjs/compiler": patch
---

Add compile-time scoped styles, CSS sidecar output, style class maps, and style refs to both TSRX frontends.
5 changes: 5 additions & 0 deletions .changeset/support-tsrx-server-function-composition.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/compiler": patch
---

Support composing lowered `.tsrx` modules with the server-function directive transform while preserving path-stable function IDs.
5 changes: 5 additions & 0 deletions .changeset/support-tsrx-source-maps.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"@solidjs/compiler": patch
---

Emit native TSRX source maps against authored `.tsrx` locations while leaving compiler-generated projection ranges unmapped.
6 changes: 6 additions & 0 deletions .changeset/tsrx-syntax-frontend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"@solidjs/babel-plugin": minor
"@solidjs/compiler": minor
---

Add an experimental TSRX syntax frontend to both compilers. `.tsrx` sources (routed by filename with the new `syntax: "auto" | "jsx" | "tsrx"` option) desugar `@if`/`@else`, `@for … @empty`, `@switch`/`@case`, `@try`/`@catch`/`@pending`, `@{}` statement containers, and lazy destructuring (`&{}`/`&[]`) into the shared Solid JSX lowering, producing byte-identical output from both compilers. The Babel plugin loads the optional `@tsrx/core` peer dependency lazily; the native compiler ships the frontend behind the default-on `tsrx` cargo feature (statement containers in expression position are rejected with a structured diagnostic pending upstream oxc-tsrx support).
243 changes: 243 additions & 0 deletions documentation/tsrx/frontend-notes.md

Large diffs are not rendered by default.

519 changes: 519 additions & 0 deletions documentation/tsrx/tsrx-specification-snapshot.md

Large diffs are not rendered by default.

45 changes: 38 additions & 7 deletions packages/babel-plugin/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ Omitted options are the Solid 2.0 defaults that used to live in `babel-preset-so
hydratable: true
}
]
]
];
}
```

Expand All @@ -99,6 +99,13 @@ Omitted options are the Solid 2.0 defaults that used to live in `babel-preset-so

Runtime module the compiled output imports helpers from. Use the same module for SSR; switch `generate` instead.

### syntax

- Type: `'auto' | 'jsx' | 'tsrx'`
- Default: `'auto'`

Source syntax frontend. `"auto"` routes `.tsrx` files through the TSRX frontend and everything else through standard JSX; `"tsrx"` forces TSRX for every file; `"jsx"` disables TSRX routing entirely. See [TSRX](#tsrx-experimental).

### generate

- Type: `'dom' | 'ssr' | 'universal' | 'dynamic'`
Expand Down Expand Up @@ -206,12 +213,7 @@ Restrict JSX transformation to files whose `@jsxImportSource` pragma matches.

```js
{
plugins: [
[
"@solidjs/babel-plugin",
{ requireImportSource: "@solidjs/web" }
]
]
plugins: [["@solidjs/babel-plugin", { requireImportSource: "@solidjs/web" }]];
}
```

Expand All @@ -234,6 +236,35 @@ Inline style attributes in templates when the value is a string or `Record<strin

SSR-only: emit behavior-claim (`_bnd`) markers for `ref` / `on*` on intrinsic elements.

## TSRX (experimental)

TSRX (TypeScript Render Extensions) is a syntax for declarative UI. `.tsrx` sources desugar to the same Solid JSX this plugin already compiles: `@if`/`@else`, `@for … @empty`, `@switch`/`@case`, and `@try`/`@catch`/`@pending` lower to the corresponding control-flow components (`Show`, `For`, `Switch`/`Match`, `Errored`, `Loading`), `@{}` statement containers mix setup statements with rendered elements, and lazy destructuring (`&{ }` / `&[ ]`) defers property access to preserve reactivity.

```tsrx
export function TodoList({ items }) @{
<ul>
<style>
li { padding-block: 0.25rem; }
</style>
@for (const item of items; index i; key item.id) {
<li>{i + 1}. {item.text}</li>
} @empty {
<li>No todos</li>
}
</ul>
}
```

Requirements and behavior:

- Compiling `.tsrx` sources requires the optional peer dependency `@tsrx/core` and Node.js >= 22.12. It loads lazily on first TSRX routing, so plain JSX users never pay for it.
- Routing is filename-based by default (`syntax: "auto"`), so Babel must receive a `filename`.
- Desugared constructs rely on the `builtIns` auto-imports, so those components must exist in `moduleName`.
- Scoped `<style>` blocks are removed at compile time, matching native and dynamic elements receive a `tsrx-<hash>` class, and the scoped/pruned stylesheet is returned as `result.metadata.css` with `result.metadata.cssHash`. Style expressions produce class-map objects, `<style ref={styles}>` initializes a class map, and `:global(...)` opts selectors out of scoping. The plugin emits no runtime style helper; a bundler integration must emit the CSS metadata.
- Lazy patterns support synchronous and asynchronous arrow parameters, nested, renamed, and computed bindings, JavaScript-style defaults, object/array rest, and standalone `&{ … } = value;` / `&[ … ] = value;` statements. Defaults apply only when the deferred value is `undefined`; rest bindings are fresh read-only views. Standalone assignment patterns do not yet accept defaults.
- Destructured bindings in keyed `@for` loops and `@catch` clauses stay deferred against Solid's item and error accessors, including nested patterns, defaults, computed keys, and rest.
- The native compiler ([`@solidjs/compiler`](../compiler)) compiles the same sources to byte-identical output.

## Special Binding

### ref
Expand Down
11 changes: 9 additions & 2 deletions packages/babel-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,12 +28,19 @@
"validate-html-nesting": "^1.2.1"
},
"peerDependencies": {
"@babel/core": "^7.20.12"
"@babel/core": "^7.20.12",
"@tsrx/core": "0.1.63"
},
"peerDependenciesMeta": {
"@tsrx/core": {
"optional": true
}
},
"devDependencies": {
"@babel/core": "^7.29.0",
"@solidjs/web": "workspace:*",
"@rollup/plugin-babel": "7.0.0",
"@solidjs/web": "workspace:*",
"@tsrx/core": "0.1.63",
"@types/babel__core": "^7.20.5",
"@types/babel__traverse": "^7.28.0"
}
Expand Down
3 changes: 2 additions & 1 deletion packages/babel-plugin/rollup.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,8 @@ export default {
"@babel/plugin-syntax-jsx",
"@babel/helper-module-imports",
"@babel/types",
"html-entities"
"html-entities",
"@tsrx/core"
],
output: {
file: "index.js",
Expand Down
6 changes: 6 additions & 0 deletions packages/babel-plugin/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ export interface RendererConfig {

export interface PluginConfig {
moduleName: string;
/** Source syntax frontend: "auto" routes `.tsrx` files through the TSRX
* parser and everything else through standard JSX; "tsrx" forces TSRX for
* every file; "jsx" disables TSRX routing entirely. TSRX support is
* experimental and requires the optional `@tsrx/core` peer dependency. */
syntax: "auto" | "jsx" | "tsrx";
generate: "dom" | "ssr" | "universal" | "dynamic";
hydratable: boolean;
dev: boolean;
Expand Down Expand Up @@ -41,6 +46,7 @@ export interface PluginConfig {

const config: PluginConfig = {
moduleName: "@solidjs/web",
syntax: "auto",
generate: "dom",
hydratable: false,
dev: false,
Expand Down
17 changes: 16 additions & 1 deletion packages/babel-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,21 +2,36 @@ import SyntaxJSX from "@babel/plugin-syntax-jsx";
import { transformJSX } from "./shared/transform";
import postprocess from "./shared/postprocess";
import preprocess from "./shared/preprocess";
import { isTsrxSource, parseTsrx, type SyntaxOption } from "./tsrx";
import type * as t from "@babel/types";
import type { Visitor } from "@babel/traverse";
import type { PluginPass } from "./types";

type JSXPluginSyntax = {
manipulateOptions(opts: unknown, parserOpts: { plugins: string[] }): void;
};

export default (): {
type ParserOptions = { sourceFileName?: string };
type ParseFn = (code: string, parserOpts: ParserOptions) => t.File;

export default (
_api?: unknown,
options: { syntax?: SyntaxOption } = {}
): {
name: string;
inherits: () => JSXPluginSyntax;
parserOverride: (code: string, parserOpts: ParserOptions, parse: ParseFn) => t.File;
visitor: Visitor<PluginPass>;
} => {
return {
name: "@solidjs/babel-plugin",
inherits: SyntaxJSX.default,
parserOverride(code, parserOpts, parse) {
if (isTsrxSource(options.syntax, parserOpts?.sourceFileName)) {
return parseTsrx(code, parserOpts?.sourceFileName);
}
return parse(code, parserOpts);
},
visitor: {
JSXElement: transformJSX,
JSXFragment: transformJSX,
Expand Down
6 changes: 6 additions & 0 deletions packages/babel-plugin/src/shared/preprocess.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,12 @@ import type { BabelHubWithMetadata, PluginPass } from "../types";

export default (path: NodePath<t.Program>, state: PluginPass) => {
const file = (path.hub as unknown as BabelHubWithMetadata).file;
const parsedMetadata = file.ast.tsrxStyle;
if (parsedMetadata) {
file.metadata.css = parsedMetadata.css;
file.metadata.cssHash = parsedMetadata.cssHash;
delete file.ast.tsrxStyle;
}
const merged = (file.metadata.config = Object.assign({}, config, state.opts));
const lib = merged.requireImportSource;
if (lib) {
Expand Down
Loading