@solidjs/compiler 2.0.0-rc.5 → 2.0.0-rc.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -78,14 +78,38 @@ const result = transform(source, {
78
78
  });
79
79
  ```
80
80
 
81
+ ### TSRX (experimental)
82
+
83
+ TSRX (TypeScript Render Extensions) is a syntax for declarative UI whose constructs (`@if`/`@else`, `@for … @empty`, `@switch`/`@case`, `@try`/`@catch`/`@pending`, and `@{}` statement containers) desugar to the Solid control-flow components. `.tsrx` filenames route through the TSRX frontend automatically and compile to the same output as `@solidjs/babel-plugin`'s TSRX support, byte for byte.
84
+
85
+ ```js
86
+ const result = transform(tsrxSource, { filename: "App.tsrx" });
87
+ // TSRX <style> blocks are extracted alongside the JavaScript:
88
+ result.css;
89
+ result.cssHash;
90
+ ```
91
+
92
+ Routing follows the filename by default (`syntax: "auto"`); pass `syntax: "tsrx"` or `syntax: "jsx"` to force a frontend regardless of filename. No extra install is needed — the shipped binaries include the frontend (Rust embedders can disable the default `tsrx` cargo feature).
93
+
94
+ Scoped `<style>` blocks are compile-time only. The compiler removes the style element, adds its `tsrx-<hash>` class to matching native and dynamic elements, scopes and prunes the CSS, and returns the stylesheet in `css` with its scope identifier in `cssHash`. Style expressions produce class-map objects, `<style ref={styles}>` initializes the requested class map, and `:global(...)` opts individual selectors out of scoping. A bundler integration must emit the returned CSS; the core compiler does not inject a runtime style helper.
95
+
96
+ Solid rejects authored TSRX lazy destructuring (`&{ … }` / `&[ … ]`). Keep accessor calls and reactive property reads explicit in Solid source.
97
+
98
+ 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.
99
+
100
+ The frontend uses the community [oxc-tsrx](https://github.com/tsrx-org/oxc) parser at a pinned revision. Statement containers can be used as function bodies, statements, expressions (`const x = @{ … }`), and JSX children or expression containers. See `documentation/tsrx/frontend-notes.md` in the repository for the full frontend notes.
101
+
102
+ `projectTsrxForTypecheck(source, { filename })` is an experimental compiler-owned projection for editor and typecheck integrations. It returns independently typecheckable post-rewrite TSX without running the DOM/SSR/universal transforms, injecting collision-safe imports for generated Solid control-flow and dynamic-element helpers. The tooling-only path recovers common incomplete editor snapshots while normal compilation remains strict. The result also includes an authored `.tsrx` source map, exact equal-text `mappings`, processed `css`/`cssHash`, and parser-authored embedded CSS/raw-script regions. Mapping and embedded offsets use JavaScript UTF-16 string coordinates. The ranges deliberately omit generated-only text; host adapters such as Volar attach feature capabilities to them.
103
+
81
104
  ### Source maps
82
105
 
83
- Pass `sourceMap: true` to receive a JSON source map string in `result.map`.
106
+ Pass `sourceMap: true` to receive a JSON source map string in `result.map`. For TSRX, the compiler composes Oxc's generated-JavaScript map through the internal TSX text projection, returning the original `.tsrx` filename and source in `sources` and `sourcesContent`. Authored expressions and lazy/accessor rewrites map back to their TSRX locations; projection-only scaffolding remains explicitly unmapped rather than being attributed to nearby syntax.
84
107
 
85
108
  ### Options
86
109
 
87
110
  - `filename`
88
111
  - `moduleName` (default `"@solidjs/web"`)
112
+ - `syntax`: `"auto"`, `"jsx"`, or `"tsrx"` (default `"auto"` — routes `.tsrx` filenames through the TSRX frontend)
89
113
  - `generate`: `"dom"`, `"ssr"`, `"universal"`, or `"dynamic"` (default `"dom"`)
90
114
  - `hydratable`
91
115
  - `dev`
@@ -110,7 +134,7 @@ Pass `sourceMap: true` to receive a JSON source map string in `result.map`.
110
134
 
111
135
  ### Server function directives (experimental)
112
136
 
113
- `transformDirectives(code, options)` is a second pass for `"use server"`. It applies to plain `.js`/`.ts` as well as JSX/TSX.
137
+ `transformDirectives(code, options)` is a second pass for `"use server"`. It accepts ordinary JavaScript/TypeScript, including JSX/TSX. For a `.tsrx` module, run `transform()` first, then pass its generated code to `transformDirectives()` with the same original `.tsrx` filename so function IDs use the manifest path. `transformDirectives()` does not parse raw TSRX syntax itself.
114
138
 
115
139
  ```js
116
140
  const { transformDirectives } = require("@solidjs/compiler");
@@ -133,16 +157,25 @@ The runtime module defaults to `@solidjs/web/server-functions`. Function IDs use
133
157
  The crate also exposes a host-independent Rust API. The crate name is `solidjs-compiler`; the Node `transform()` delegates to the same core.
134
158
 
135
159
  ```rust
136
- use solidjs_compiler::{compile, CompileOptions};
160
+ use solidjs_compiler::{
161
+ compile, project_tsrx_for_typecheck, CompileOptions,
162
+ TsrxTypecheckProjectionOptions,
163
+ };
137
164
 
138
165
  let output = compile(
139
166
  "const view = <div>{name()}</div>;",
140
167
  &CompileOptions::default(),
141
168
  )?;
169
+
170
+ let tsrx_source = "export function View() @{ <div /> }";
171
+ let virtual_tsx =
172
+ project_tsrx_for_typecheck(tsrx_source, &TsrxTypecheckProjectionOptions::default())?;
142
173
  ```
143
174
 
144
175
  `CompileOptions::default()` uses `module_name: "@solidjs/web"` and the same control-flow `built_ins` as the Babel plugin. Build with `--no-default-features` when embedding without the Node-API adapter.
145
176
 
177
+ The unstable Rust typecheck projection reports embedded ranges in authored UTF-8 bytes. The N-API adapter converts those ranges to UTF-16 code units for JavaScript tooling.
178
+
146
179
  > **Stability:** the Rust API is unstable while the compiler is pre-1.0. Options, output, and error types may change in any release — pin an exact revision when embedding it.
147
180
 
148
181
  ## Performance
@@ -151,9 +184,9 @@ Compared against `@solidjs/babel-plugin` compiling identical sources under ident
151
184
 
152
185
  | Workload | babel-plugin | compiler | Speedup |
153
186
  | ----------------------------------------------- | -----------: | -------: | ------: |
154
- | Fixture corpus (88 files, 175 KB, all 10 modes) | 440 ms | 19 ms | 23x |
155
- | 129 KB single module | 545 ms | 9.4 ms | 58x |
156
- | 1 MB single module | 24,975 ms | 70 ms | 355x |
187
+ | Fixture corpus (88 files, 175 KB, all 10 modes) | 440 ms | 19 ms | 23x |
188
+ | 129 KB single module | 545 ms | 9.4 ms | 58x |
189
+ | 1 MB single module | 24,975 ms | 70 ms | 355x |
157
190
 
158
191
  Native throughput stays roughly flat as input grows, while Babel's per-file cost grows super-linearly.
159
192
 
package/index.js CHANGED
@@ -12,16 +12,60 @@ function transform(code, options) {
12
12
 
13
13
  const nativeOptions = validateOptions(code, options);
14
14
  const result = native.transform(code, nativeOptions);
15
- return {
15
+ const output = {
16
16
  code: result.code,
17
17
  map: result.map ?? null
18
18
  };
19
+ // Preserve the established JSX result shape. Native TSRX transforms always
20
+ // return a CSS string (including `""` when no styles are present), which
21
+ // makes the sidecar fields a route-specific extension.
22
+ if (result.css != null) {
23
+ output.css = result.css;
24
+ output.cssHash = result.cssHash ?? null;
25
+ }
26
+ return output;
19
27
  }
20
28
 
21
29
  function transformAsync(code, options) {
22
30
  return Promise.resolve().then(() => transform(code, options));
23
31
  }
24
32
 
33
+ function projectTsrxForTypecheck(code, options) {
34
+ if (typeof code !== "string") {
35
+ throw new TypeError(
36
+ "@solidjs/compiler projectTsrxForTypecheck() expects source code as a string"
37
+ );
38
+ }
39
+ const nativeOptions = validateTypecheckProjectionOptions(options);
40
+ const result = native.projectTsrxForTypecheck(code, nativeOptions);
41
+ return {
42
+ code: result.code,
43
+ map: result.map,
44
+ mappings: result.mappings,
45
+ css: result.css,
46
+ cssHash: result.cssHash ?? null,
47
+ embeddedRegions: result.embeddedRegions
48
+ };
49
+ }
50
+
51
+ function validateTypecheckProjectionOptions(options) {
52
+ if (options == null) return options;
53
+ if (typeof options !== "object" || Array.isArray(options)) {
54
+ throw new TypeError(
55
+ "@solidjs/compiler projectTsrxForTypecheck() expects options to be an object"
56
+ );
57
+ }
58
+ for (const key of Object.keys(options)) {
59
+ if (key !== "filename") {
60
+ throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
61
+ }
62
+ }
63
+ if (options.filename !== undefined && typeof options.filename !== "string") {
64
+ throw new TypeError("@solidjs/compiler `filename` option must be a string");
65
+ }
66
+ return options;
67
+ }
68
+
25
69
  function transformDirectives(code, options) {
26
70
  if (typeof code !== "string") {
27
71
  throw new TypeError("@solidjs/compiler transformDirectives() expects source code as a string");
@@ -427,6 +471,7 @@ function isMissingPackage(error, packageName) {
427
471
  module.exports = {
428
472
  transform,
429
473
  transformAsync,
474
+ projectTsrxForTypecheck,
430
475
  transformDirectives,
431
476
  transformDirectivesAsync,
432
477
  transformLazy,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@solidjs/compiler",
3
3
  "description": "Solid's native Oxc JSX compiler",
4
- "version": "2.0.0-rc.5",
4
+ "version": "2.0.0-rc.6",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -30,7 +30,7 @@
30
30
  "bench": "pnpm run build && node scripts/bench.mjs",
31
31
  "lint": "cargo clippy --manifest-path ./Cargo.toml -- -D warnings",
32
32
  "test": "pnpm run test:rust && pnpm run build:debug && vitest run",
33
- "test:rust": "cargo test --manifest-path ./Cargo.toml && cargo test --manifest-path ./Cargo.toml --no-default-features",
33
+ "test:rust": "cargo test --manifest-path ./Cargo.toml && cargo test --manifest-path ./Cargo.toml --no-default-features && cargo test --manifest-path ./Cargo.toml --no-default-features --features tsrx",
34
34
  "artifacts": "napi artifacts",
35
35
  "napi:version": "napi version && node ./sync-optional-deps.mjs",
36
36
  "create-npm-dirs": "napi create-npm-dirs"
@@ -62,11 +62,11 @@
62
62
  "emnapi": "^1.11.3"
63
63
  },
64
64
  "optionalDependencies": {
65
- "@solidjs/compiler-darwin-x64": "2.0.0-rc.5",
66
- "@solidjs/compiler-darwin-arm64": "2.0.0-rc.5",
67
- "@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.5",
68
- "@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.5",
69
- "@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.5",
70
- "@solidjs/compiler-wasm32-wasi": "2.0.0-rc.5"
65
+ "@solidjs/compiler-darwin-x64": "2.0.0-rc.6",
66
+ "@solidjs/compiler-darwin-arm64": "2.0.0-rc.6",
67
+ "@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.6",
68
+ "@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.6",
69
+ "@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.6",
70
+ "@solidjs/compiler-wasm32-wasi": "2.0.0-rc.6"
71
71
  }
72
72
  }
package/types.d.ts CHANGED
@@ -2,6 +2,13 @@ export interface TransformOptions {
2
2
  filename?: string;
3
3
  /** Default `"@solidjs/web"`. */
4
4
  moduleName?: string;
5
+ /**
6
+ * Source syntax frontend, matching `@solidjs/babel-plugin`: `"auto"`
7
+ * (default) routes `.tsrx` filenames through the TSRX frontend and
8
+ * everything else through standard JSX; `"tsrx"` and `"jsx"` force a
9
+ * frontend regardless of filename. TSRX support is experimental.
10
+ */
11
+ syntax?: "auto" | "jsx" | "tsrx";
5
12
  generate?: "dom" | "ssr" | "universal" | "dynamic";
6
13
  hydratable?: boolean;
7
14
  dev?: boolean;
@@ -44,6 +51,10 @@ export interface RendererOption {
44
51
  export interface TransformResult {
45
52
  code: string;
46
53
  map?: string | null;
54
+ /** Extracted scoped CSS for TSRX sources. */
55
+ css?: string | null;
56
+ /** Space-separated TSRX scope hashes. */
57
+ cssHash?: string | null;
47
58
  }
48
59
 
49
60
  export function transform(code: string, options?: TransformOptions | null): TransformResult;
@@ -52,6 +63,49 @@ export function transformAsync(
52
63
  options?: TransformOptions | null
53
64
  ): Promise<TransformResult>;
54
65
 
66
+ export interface ProjectTsrxForTypecheckOptions {
67
+ filename?: string;
68
+ }
69
+
70
+ export interface TsrxTypecheckEmbeddedRegion {
71
+ kind: "css" | "script";
72
+ /** Authored JavaScript string offset in UTF-16 code units. */
73
+ start: number;
74
+ /** Authored JavaScript string offset in UTF-16 code units. */
75
+ end: number;
76
+ content: string;
77
+ }
78
+
79
+ export interface TsrxTypecheckMapping {
80
+ /** Authored JavaScript string offset in UTF-16 code units. */
81
+ sourceStart: number;
82
+ /** Generated JavaScript string offset in UTF-16 code units. */
83
+ generatedStart: number;
84
+ sourceLength: number;
85
+ generatedLength: number;
86
+ }
87
+
88
+ export interface TsrxTypecheckProjectionResult {
89
+ /** Valid post-semantic-rewrite TypeScript/TSX. */
90
+ code: string;
91
+ /** JSON source map from virtual TSX back to the authored `.tsrx` source. */
92
+ map: string;
93
+ /** Exact equal-text ranges suitable for editor feature mappings. */
94
+ mappings: TsrxTypecheckMapping[];
95
+ css: string;
96
+ cssHash: string | null;
97
+ embeddedRegions: TsrxTypecheckEmbeddedRegion[];
98
+ }
99
+
100
+ /**
101
+ * Experimental compiler-owned TSRX projection for typechecking and editor
102
+ * tooling. This API is host-independent and does not run a runtime renderer.
103
+ */
104
+ export function projectTsrxForTypecheck(
105
+ code: string,
106
+ options?: ProjectTsrxForTypecheckOptions | null
107
+ ): TsrxTypecheckProjectionResult;
108
+
55
109
  export interface DirectiveImportDefinition {
56
110
  kind?: "named" | "default";
57
111
  name?: string;