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

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.
Files changed (4) hide show
  1. package/README.md +39 -6
  2. package/index.js +47 -18
  3. package/package.json +8 -8
  4. package/types.d.ts +54 -9
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");
@@ -234,10 +278,7 @@ const nativeOptionKeys = new Set([
234
278
  "omitLastClosingTag",
235
279
  "serverComponents",
236
280
  "builtIns",
237
- "renderers",
238
- // Patch-mode dual driver (stage 2, dormant by default): accepted so an
239
- // explicit opt-in reaches the native core (napi maps to patch_driver).
240
- "patchDriver"
281
+ "renderers"
241
282
  ]);
242
283
 
243
284
  function validateOptions(code, options) {
@@ -280,19 +321,6 @@ function validateOptions(code, options) {
280
321
  nativeOptions.validate = value;
281
322
  continue;
282
323
  }
283
- if (key === "patchDriver") {
284
- if (typeof value !== "string" && typeof value !== "boolean") {
285
- throw new TypeError(
286
- "@solidjs/compiler `patchDriver` option must be a string import name or boolean"
287
- );
288
- }
289
- // The napi wrapper mapping collapses boolean `true` into
290
- // Wrapper::Default, which patch_driver treats as disabled (dormant
291
- // default). Normalize the boolean opt-in to the default import name so
292
- // it survives the native mapping.
293
- nativeOptions.patchDriver = value === true ? "patchDriver" : value;
294
- continue;
295
- }
296
324
  if (nativeOptionKeys.has(key)) {
297
325
  if (key === "renderers") validateRenderers(value);
298
326
  nativeOptions[key] = value;
@@ -427,6 +455,7 @@ function isMissingPackage(error, packageName) {
427
455
  module.exports = {
428
456
  transform,
429
457
  transformAsync,
458
+ projectTsrxForTypecheck,
430
459
  transformDirectives,
431
460
  transformDirectivesAsync,
432
461
  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.7",
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.7",
66
+ "@solidjs/compiler-darwin-arm64": "2.0.0-rc.7",
67
+ "@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.7",
68
+ "@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.7",
69
+ "@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.7",
70
+ "@solidjs/compiler-wasm32-wasi": "2.0.0-rc.7"
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;
@@ -20,15 +27,6 @@ export interface TransformOptions {
20
27
  omitNestedClosingTags?: boolean;
21
28
  omitLastClosingTag?: boolean;
22
29
  serverComponents?: boolean;
23
- /**
24
- * Patch-mode dual driver (dormant by default): `true` or an import name
25
- * (`"patchDriver"`) opts compiled templates whose bindings are pure member
26
- * reads of one subject into the store patch channel. The loader normalizes
27
- * `true` to the default import name (the napi wrapper mapping treats bare
28
- * booleans as "default", which this option reads as disabled).
29
- * @default false
30
- */
31
- patchDriver?: boolean | string;
32
30
  /** Default `["For", "Show", "Switch", "Match", "Loading", "Reveal", "Portal", "Repeat", "Dynamic", "Errored"]`. */
33
31
  builtIns?: string[];
34
32
  requireImportSource?: false | string;
@@ -44,6 +42,10 @@ export interface RendererOption {
44
42
  export interface TransformResult {
45
43
  code: string;
46
44
  map?: string | null;
45
+ /** Extracted scoped CSS for TSRX sources. */
46
+ css?: string | null;
47
+ /** Space-separated TSRX scope hashes. */
48
+ cssHash?: string | null;
47
49
  }
48
50
 
49
51
  export function transform(code: string, options?: TransformOptions | null): TransformResult;
@@ -52,6 +54,49 @@ export function transformAsync(
52
54
  options?: TransformOptions | null
53
55
  ): Promise<TransformResult>;
54
56
 
57
+ export interface ProjectTsrxForTypecheckOptions {
58
+ filename?: string;
59
+ }
60
+
61
+ export interface TsrxTypecheckEmbeddedRegion {
62
+ kind: "css" | "script";
63
+ /** Authored JavaScript string offset in UTF-16 code units. */
64
+ start: number;
65
+ /** Authored JavaScript string offset in UTF-16 code units. */
66
+ end: number;
67
+ content: string;
68
+ }
69
+
70
+ export interface TsrxTypecheckMapping {
71
+ /** Authored JavaScript string offset in UTF-16 code units. */
72
+ sourceStart: number;
73
+ /** Generated JavaScript string offset in UTF-16 code units. */
74
+ generatedStart: number;
75
+ sourceLength: number;
76
+ generatedLength: number;
77
+ }
78
+
79
+ export interface TsrxTypecheckProjectionResult {
80
+ /** Valid post-semantic-rewrite TypeScript/TSX. */
81
+ code: string;
82
+ /** JSON source map from virtual TSX back to the authored `.tsrx` source. */
83
+ map: string;
84
+ /** Exact equal-text ranges suitable for editor feature mappings. */
85
+ mappings: TsrxTypecheckMapping[];
86
+ css: string;
87
+ cssHash: string | null;
88
+ embeddedRegions: TsrxTypecheckEmbeddedRegion[];
89
+ }
90
+
91
+ /**
92
+ * Experimental compiler-owned TSRX projection for typechecking and editor
93
+ * tooling. This API is host-independent and does not run a runtime renderer.
94
+ */
95
+ export function projectTsrxForTypecheck(
96
+ code: string,
97
+ options?: ProjectTsrxForTypecheckOptions | null
98
+ ): TsrxTypecheckProjectionResult;
99
+
55
100
  export interface DirectiveImportDefinition {
56
101
  kind?: "named" | "default";
57
102
  name?: string;