@solidjs/compiler 2.0.0-rc.10
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 +222 -0
- package/index.js +531 -0
- package/package.json +72 -0
- package/types.d.ts +290 -0
package/README.md
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
# @solidjs/compiler
|
|
2
|
+
|
|
3
|
+
Solid 2.0's native Oxc JSX compiler. Integrations call `transform()` once per source module; this package is not a Vite, Rollup, or Babel plugin by itself. The JavaScript fallback is [`@solidjs/babel-plugin`](../babel-plugin).
|
|
4
|
+
|
|
5
|
+
> **Solid 2.0 (Release Candidate).** Pin exact versions. The Node `transform()` interface is the supported public contract; the Rust `compile` API is unstable.
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @solidjs/compiler
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The package ships prebuilt native binaries as optional per-platform packages (`@solidjs/compiler-darwin-arm64`, `-darwin-x64`, `-linux-x64-gnu`, `-linux-arm64-gnu`, `-win32-x64-msvc`). Your package manager installs the one matching your platform. On other platforms, build from source with `pnpm run build` inside `packages/compiler` (requires a Rust toolchain).
|
|
14
|
+
|
|
15
|
+
### WebAssembly and StackBlitz
|
|
16
|
+
|
|
17
|
+
A WASI fallback covers environments such as StackBlitz WebContainers, where Node reports a native platform but cannot load `.node` addons. Package managers install `@solidjs/compiler-wasm32-wasi` as an optional dependency. The package entry prefers a native binding and falls back to WASI when native addons are unavailable.
|
|
18
|
+
|
|
19
|
+
- `NAPI_RS_FORCE_WASI=error` requires the WASI binding (useful in tests).
|
|
20
|
+
- `SOLID_COMPILER_NATIVE=/path/to/binding.node` loads an explicit native addon.
|
|
21
|
+
|
|
22
|
+
## Usage
|
|
23
|
+
|
|
24
|
+
Omitted options match `@solidjs/babel-plugin` (and the old `babel-preset-solid`): `moduleName` is `"@solidjs/web"`, `generate` is `"dom"`, and control-flow tags (`For`, `Show`, `Switch`, `Match`, `Loading`, `Reveal`, `Portal`, `Repeat`, `Dynamic`, `Errored`) are auto-imported from that module.
|
|
25
|
+
|
|
26
|
+
```js
|
|
27
|
+
const { transform } = require("@solidjs/compiler");
|
|
28
|
+
|
|
29
|
+
const result = transform(`const view = <div>Hello</div>;`, {
|
|
30
|
+
filename: "App.jsx"
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
console.log(result.code);
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
`transformAsync()` is the same transform behind a promise, for integration points that expect one.
|
|
37
|
+
|
|
38
|
+
### Client DOM
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
const result = transform(source, {
|
|
42
|
+
filename: "App.jsx",
|
|
43
|
+
generate: "dom",
|
|
44
|
+
hydratable: true
|
|
45
|
+
});
|
|
46
|
+
```
|
|
47
|
+
|
|
48
|
+
`contextToCustomElements` defaults to `true`. Use `dev: true` with `hydratable: true` to emit hydration walk helpers such as `getFirstChild` / `getNextSibling`.
|
|
49
|
+
|
|
50
|
+
### SSR
|
|
51
|
+
|
|
52
|
+
SSR still imports runtime helpers from `@solidjs/web`. Set `generate: "ssr"` (and `hydratable: true` when the client will hydrate).
|
|
53
|
+
|
|
54
|
+
```js
|
|
55
|
+
const result = transform(source, {
|
|
56
|
+
filename: "entry-server.jsx",
|
|
57
|
+
generate: "ssr",
|
|
58
|
+
hydratable: true
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
### Universal and dynamic
|
|
63
|
+
|
|
64
|
+
Custom renderers use `generate: "universal"` with `moduleName` pointing at the renderer package. Dynamic mode uses that renderer as the fallback and can route a configured set of native tags to the DOM renderer.
|
|
65
|
+
|
|
66
|
+
```js
|
|
67
|
+
const result = transform(source, {
|
|
68
|
+
filename: "hybrid.jsx",
|
|
69
|
+
moduleName: "solid-custom-dom",
|
|
70
|
+
generate: "dynamic",
|
|
71
|
+
renderers: [
|
|
72
|
+
{
|
|
73
|
+
name: "dom",
|
|
74
|
+
moduleName: "@solidjs/web",
|
|
75
|
+
elements: ["div", "span", "button", "input"]
|
|
76
|
+
}
|
|
77
|
+
]
|
|
78
|
+
});
|
|
79
|
+
```
|
|
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
|
+
Control-flow bindings pass through exactly as authored. `@for … index` and `@for … key` hand the callback the item as an accessor (and the index as an accessor under a custom key), and `@catch (err, reset)` receives Solid's `ErrorAccessor` — write `item()`, `i()`, `err()` as you would in JSX. Destructuring a binding in one of those positions is rejected with a diagnostic, since there is nothing to destructure; the default keyed `@for` item is a raw value and destructures as usual.
|
|
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 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
|
+
|
|
104
|
+
### Source maps
|
|
105
|
+
|
|
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.
|
|
107
|
+
|
|
108
|
+
### Options
|
|
109
|
+
|
|
110
|
+
- `filename`
|
|
111
|
+
- `moduleName` (default `"@solidjs/web"`)
|
|
112
|
+
- `syntax`: `"auto"`, `"jsx"`, or `"tsrx"` (default `"auto"` — routes `.tsrx` filenames through the TSRX frontend)
|
|
113
|
+
- `generate`: `"dom"`, `"ssr"`, `"universal"`, or `"dynamic"` (default `"dom"`)
|
|
114
|
+
- `hydratable`
|
|
115
|
+
- `dev`
|
|
116
|
+
- `sourceNames` (`boolean | { components?: boolean; bindings?: boolean }`, default follows `dev`): names as written in source, carried into output so dev/observe runtimes can label the reactive graph after minification. Unset, every kind is on when `dev: true` and off otherwise; `true`/`false` sets every kind (`sourceNames: false` opts a dev build out); the object form picks, and each kind it leaves unspecified follows `dev`. Production output (`dev: false`) is byte-identical with the option set or unset. Same shape and defaults as `@solidjs/babel-plugin`'s option. `components` emits the source tag name as `createComponent`'s third argument (`createComponent(Home, props, "Home")`) — DOM and SSR output (SSR keeps the `createComponent` call it otherwise inlines to `Comp(props)`); not universal or dynamic. `bindings` names every compiled binding effect by what it writes — `effect(…, { name: "span.textContent" })`, a hole `insert(el, v, undefined, undefined, { name: "div.children" })`, a spread `spread(el, props, false, undefined, "div")` (labelled `div.spread` / `div.children` by the runtime) — DOM output only. The production runtimes ignore the names. These are the JSX-level kinds only: naming the primitives themselves is the separate `transformSourceNames` pass below, which the build tool runs on every module independently of the JSX compiler
|
|
117
|
+
- `sourceMap`
|
|
118
|
+
- `contextToCustomElements` (default `true`)
|
|
119
|
+
- `delegateEvents`
|
|
120
|
+
- `delegatedEvents`
|
|
121
|
+
- `omitQuotes`
|
|
122
|
+
- `omitAttributeSpacing`
|
|
123
|
+
- `inlineStyles`
|
|
124
|
+
- `effectWrapper`: import name string, or `false` to disable
|
|
125
|
+
- `memoWrapper`: import name string, or `false` to disable
|
|
126
|
+
- `wrapConditionals`
|
|
127
|
+
- `staticMarker`
|
|
128
|
+
- `validate`
|
|
129
|
+
- `omitNestedClosingTags`
|
|
130
|
+
- `omitLastClosingTag`
|
|
131
|
+
- `builtIns` (default `["For", "Show", "Switch", "Match", "Loading", "Reveal", "Portal", "Repeat", "Dynamic", "Errored"]`)
|
|
132
|
+
- `requireImportSource`
|
|
133
|
+
- `serverComponents`
|
|
134
|
+
- `hoistProps` (default `true`; SSR only)
|
|
135
|
+
- `renderers`
|
|
136
|
+
|
|
137
|
+
### Server function directives (experimental)
|
|
138
|
+
|
|
139
|
+
`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.
|
|
140
|
+
|
|
141
|
+
```js
|
|
142
|
+
const { transformDirectives } = require("@solidjs/compiler");
|
|
143
|
+
|
|
144
|
+
const result = transformDirectives(source, {
|
|
145
|
+
filename: "/project/src/api.ts",
|
|
146
|
+
root: "/project",
|
|
147
|
+
mode: "server" // or "client"
|
|
148
|
+
});
|
|
149
|
+
|
|
150
|
+
result.valid; // false when no directive matched — keep the original module
|
|
151
|
+
result.code;
|
|
152
|
+
result.functions; // [{ id, name, exports }] for manifest building
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
A function-level `"use server"` function is extracted out of its lexical position, so it may only reference its own parameters and locals, module top-level bindings, and globals. Capturing anything else is a compile error. That includes `this` and `arguments` in a marked arrow, which an arrow takes from the function it was written in. Use a `function` if the server function needs its own `this` or `arguments`.
|
|
156
|
+
|
|
157
|
+
A function-level directive only works where the pass can extract the function: a function declaration (at any nesting depth), a function expression, or an arrow with a block body. Methods, getters, and setters are never extracted, so a directive on one is a compile error rather than a directive that silently does nothing. Assign a function to a property instead.
|
|
158
|
+
|
|
159
|
+
A module-level `"use server"` module can only export server functions. Its client build is rebuilt from those exports alone, so anything else would be missing from the browser bundle. Re-exports, `export *`, class and enum exports, destructured exports, and exports declared without an initializer are compile errors naming the export and its position. Type-only and `declare` exports are erased and are fine.
|
|
160
|
+
|
|
161
|
+
The runtime module defaults to `@solidjs/web/server-functions`. Function IDs are `<name>-<xxhash32(root-relative path)>`, the same in every env. The name is the function's dotted binding path, such as `handlers.save`, built from every named container on the way down (variable bindings, property keys, class names, class members, and named functions), so an id identifies a function by where it is bound rather than by its position in the file. Adding, removing, or reordering functions does not move the ids of the others. There are also experimental `transformLazy` and `transformRefresh` passes.
|
|
162
|
+
|
|
163
|
+
### Source names for primitives
|
|
164
|
+
|
|
165
|
+
`transformSourceNames(code, { filename?, sourceMap? })` is the pass behind `@solidjs/vite-plugin`'s `sourceNames.primitives` option: it names reactive primitives after the identifier they are declared as, so the dev and observe runtimes label graph nodes `count` / `doubled` / `todos.title` instead of `signal` / `computed` / `store.title`. It is plain JavaScript in and out (JSX passes through untouched), which is why it is its own pass rather than a `transform()` option — primitives live in `.ts`/`.js` modules as much as in components. This standalone pass is the single owner of primitive naming: the build tool runs it on every module — `.ts`, `.js`, and JSX files alike — independently of which JSX compiler (this one or `@solidjs/babel-plugin`) handles the file's JSX, so there is no Babel counterpart and `transform()`'s `sourceNames` covers only the JSX-level kinds. `@solidjs/vite-plugin` (solid-vite-plugin #371, `solid: { sourceNames }`) runs it ahead of the JSX transform for the dev and `observe` postures.
|
|
166
|
+
|
|
167
|
+
```js
|
|
168
|
+
const [count, setCount] = createSignal(0); // createSignal(0, { name: "count" })
|
|
169
|
+
const doubled = createMemo(() => count() * 2); // createMemo(…, { name: "doubled" })
|
|
170
|
+
const [todos, setTodos] = createStore({ list: [] }); // createStore(…, { name: "todos" })
|
|
171
|
+
export function createCounter() {
|
|
172
|
+
const [value, setValue] = createSignal(0); // { name: "createCounter.value" }
|
|
173
|
+
…
|
|
174
|
+
}
|
|
175
|
+
function Counter() {
|
|
176
|
+
const [n, setN] = createSignal(0); // { name: "n" } — no prefix in a component
|
|
177
|
+
}
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The name comes from the first element of the array pattern, the variable binding, the object-literal property key, or the class field the call initialises; a call in any other position (a hole in the pattern, an argument, an assignment) is left alone. Inside a non-component function — anything not PascalCase: `createCounter`, `useTheme`, a method — the name is prefixed with that function's, so a composed primitive's nodes fold under it (`createCounter.value`, `createCounter.twice`); components contribute no prefix, and anonymous callbacks inherit the nearest named function. Only calls that resolve to imports from `solid-js` or `@solidjs/signals` are named (aliases and namespace imports included; a shadowing local or another library's `createSignal` is not). The pass never overrides an explicit `name`, leaves a spread or non-literal options argument alone, fills omitted positional arguments with `void 0`, and reaches the call through `as`/`satisfies`/`!` and type arguments. Named: `createSignal`, `createMemo`, `createOptimistic`, `createStore`, `createOptimisticStore`, `createProjection`. A two-argument `createStore(x, y)` is only named when `y` is a non-empty object literal of option keys (`shallow`/`name`) — otherwise it may be a derive passed by reference with its seed.
|
|
181
|
+
|
|
182
|
+
## Rust compiler core
|
|
183
|
+
|
|
184
|
+
The crate also exposes a host-independent Rust API. The crate name is `solidjs-compiler`; the Node `transform()` delegates to the same core.
|
|
185
|
+
|
|
186
|
+
```rust
|
|
187
|
+
use solidjs_compiler::{
|
|
188
|
+
compile, project_tsrx_for_typecheck, CompileOptions,
|
|
189
|
+
TsrxTypecheckProjectionOptions,
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
let output = compile(
|
|
193
|
+
"const view = <div>{name()}</div>;",
|
|
194
|
+
&CompileOptions::default(),
|
|
195
|
+
)?;
|
|
196
|
+
|
|
197
|
+
let tsrx_source = "export function View() @{ <div /> }";
|
|
198
|
+
let virtual_tsx =
|
|
199
|
+
project_tsrx_for_typecheck(tsrx_source, &TsrxTypecheckProjectionOptions::default())?;
|
|
200
|
+
```
|
|
201
|
+
|
|
202
|
+
`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.
|
|
203
|
+
|
|
204
|
+
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.
|
|
205
|
+
|
|
206
|
+
> **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.
|
|
207
|
+
|
|
208
|
+
## Performance
|
|
209
|
+
|
|
210
|
+
Compared against `@solidjs/babel-plugin` compiling identical sources under identical options (Apple M5, 10 cores, 32 GB RAM, Node 26, release build, in-process, median of 7 iterations after warmup — run `pnpm bench` in this package to reproduce):
|
|
211
|
+
|
|
212
|
+
| Workload | babel-plugin | compiler | Speedup |
|
|
213
|
+
| ----------------------------------------------- | -----------: | -------: | ------: |
|
|
214
|
+
| Fixture corpus (88 files, 175 KB, all 10 modes) | 440 ms | 19 ms | 23x |
|
|
215
|
+
| 129 KB single module | 545 ms | 9.4 ms | 58x |
|
|
216
|
+
| 1 MB single module | 24,975 ms | 70 ms | 355x |
|
|
217
|
+
|
|
218
|
+
Native throughput stays roughly flat as input grows, while Babel's per-file cost grows super-linearly.
|
|
219
|
+
|
|
220
|
+
## Architecture
|
|
221
|
+
|
|
222
|
+
Parse with Oxc, transform JSX with `VisitMut`, build replacements with `AstBuilder`, codegen once. Unsupported features are rejected rather than silently ignored. The module layout follows the Babel plugin (`shared`, `dom`, `ssr`, `universal`) where that mapping is useful.
|
package/index.js
ADDED
|
@@ -0,0 +1,531 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const native = requireBinding();
|
|
7
|
+
|
|
8
|
+
function transform(code, options) {
|
|
9
|
+
if (typeof code !== "string") {
|
|
10
|
+
throw new TypeError("@solidjs/compiler transform() expects source code as a string");
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const nativeOptions = validateOptions(code, options);
|
|
14
|
+
const result = native.transform(code, nativeOptions);
|
|
15
|
+
const output = {
|
|
16
|
+
code: result.code,
|
|
17
|
+
map: result.map ?? null
|
|
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;
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function transformAsync(code, options) {
|
|
30
|
+
return Promise.resolve().then(() => transform(code, options));
|
|
31
|
+
}
|
|
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
|
+
|
|
69
|
+
function transformDirectives(code, options) {
|
|
70
|
+
if (typeof code !== "string") {
|
|
71
|
+
throw new TypeError("@solidjs/compiler transformDirectives() expects source code as a string");
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const nativeOptions = validateDirectivesOptions(options);
|
|
75
|
+
const result = native.transformDirectives(code, nativeOptions);
|
|
76
|
+
return {
|
|
77
|
+
code: result.code,
|
|
78
|
+
map: result.map ?? null,
|
|
79
|
+
valid: result.valid,
|
|
80
|
+
functions: result.functions
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function transformDirectivesAsync(code, options) {
|
|
85
|
+
return Promise.resolve().then(() => transformDirectives(code, options));
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function transformLazy(code, options) {
|
|
89
|
+
if (typeof code !== "string") {
|
|
90
|
+
throw new TypeError("@solidjs/compiler transformLazy() expects source code as a string");
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const nativeOptions = validateLazyOptions(options);
|
|
94
|
+
const result = native.transformLazy(code, nativeOptions);
|
|
95
|
+
return {
|
|
96
|
+
code: result.code,
|
|
97
|
+
map: result.map ?? null
|
|
98
|
+
};
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
function transformLazyAsync(code, options) {
|
|
102
|
+
return Promise.resolve().then(() => transformLazy(code, options));
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function transformRefresh(code, options) {
|
|
106
|
+
if (typeof code !== "string") {
|
|
107
|
+
throw new TypeError("@solidjs/compiler transformRefresh() expects source code as a string");
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
const nativeOptions = validateRefreshOptions(options);
|
|
111
|
+
const result = native.transformRefresh(code, nativeOptions);
|
|
112
|
+
return {
|
|
113
|
+
code: result.code,
|
|
114
|
+
map: result.map ?? null
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function transformRefreshAsync(code, options) {
|
|
119
|
+
return Promise.resolve().then(() => transformRefresh(code, options));
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function transformSourceNames(code, options) {
|
|
123
|
+
if (typeof code !== "string") {
|
|
124
|
+
throw new TypeError("@solidjs/compiler transformSourceNames() expects source code as a string");
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
const nativeOptions = validateSourceNamesOptions(options);
|
|
128
|
+
const result = native.transformSourceNames(code, nativeOptions);
|
|
129
|
+
return {
|
|
130
|
+
code: result.code,
|
|
131
|
+
map: result.map ?? null
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function transformSourceNamesAsync(code, options) {
|
|
136
|
+
return Promise.resolve().then(() => transformSourceNames(code, options));
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
const sourceNamesOptionKeys = new Set(["filename", "sourceMap"]);
|
|
140
|
+
|
|
141
|
+
function validateSourceNamesOptions(options) {
|
|
142
|
+
if (options == null) return options;
|
|
143
|
+
if (typeof options !== "object" || Array.isArray(options)) {
|
|
144
|
+
throw new TypeError("@solidjs/compiler transformSourceNames() expects options to be an object");
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const nativeOptions = {};
|
|
148
|
+
for (const [key, value] of Object.entries(options)) {
|
|
149
|
+
if (!sourceNamesOptionKeys.has(key)) {
|
|
150
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
151
|
+
}
|
|
152
|
+
if (key === "filename" && typeof value !== "string") {
|
|
153
|
+
throw new TypeError("@solidjs/compiler `filename` option must be a string");
|
|
154
|
+
}
|
|
155
|
+
if (key === "sourceMap" && typeof value !== "boolean") {
|
|
156
|
+
throw new TypeError("@solidjs/compiler `sourceMap` option must be boolean");
|
|
157
|
+
}
|
|
158
|
+
nativeOptions[key] = value;
|
|
159
|
+
}
|
|
160
|
+
return nativeOptions;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
const lazyOptionKeys = new Set(["filename", "sourceMap"]);
|
|
164
|
+
|
|
165
|
+
function validateLazyOptions(options) {
|
|
166
|
+
if (options == null) return options;
|
|
167
|
+
if (typeof options !== "object" || Array.isArray(options)) {
|
|
168
|
+
throw new TypeError("@solidjs/compiler transformLazy() expects options to be an object");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const nativeOptions = {};
|
|
172
|
+
for (const [key, value] of Object.entries(options)) {
|
|
173
|
+
if (!lazyOptionKeys.has(key)) {
|
|
174
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
175
|
+
}
|
|
176
|
+
if (key === "filename" && typeof value !== "string") {
|
|
177
|
+
throw new TypeError("@solidjs/compiler `filename` option must be a string");
|
|
178
|
+
}
|
|
179
|
+
if (key === "sourceMap" && typeof value !== "boolean") {
|
|
180
|
+
throw new TypeError("@solidjs/compiler `sourceMap` option must be boolean");
|
|
181
|
+
}
|
|
182
|
+
nativeOptions[key] = value;
|
|
183
|
+
}
|
|
184
|
+
return nativeOptions;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const refreshOptionKeys = new Set([
|
|
188
|
+
"filename",
|
|
189
|
+
"bundler",
|
|
190
|
+
"fixRender",
|
|
191
|
+
"granular",
|
|
192
|
+
"jsx",
|
|
193
|
+
"importSource",
|
|
194
|
+
"sourceMap"
|
|
195
|
+
]);
|
|
196
|
+
|
|
197
|
+
// The runtime modes `solid-js/refresh` knows (its `RuntimeType`).
|
|
198
|
+
const refreshBundlers = new Set(["vite", "standard"]);
|
|
199
|
+
|
|
200
|
+
function validateRefreshOptions(options) {
|
|
201
|
+
if (options == null) return options;
|
|
202
|
+
if (typeof options !== "object" || Array.isArray(options)) {
|
|
203
|
+
throw new TypeError("@solidjs/compiler transformRefresh() expects options to be an object");
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const nativeOptions = {};
|
|
207
|
+
for (const [key, value] of Object.entries(options)) {
|
|
208
|
+
if (!refreshOptionKeys.has(key)) {
|
|
209
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
210
|
+
}
|
|
211
|
+
if ((key === "filename" || key === "importSource") && typeof value !== "string") {
|
|
212
|
+
throw new TypeError(`@solidjs/compiler \`${key}\` option must be a string`);
|
|
213
|
+
}
|
|
214
|
+
if (
|
|
215
|
+
(key === "fixRender" || key === "granular" || key === "sourceMap") &&
|
|
216
|
+
typeof value !== "boolean"
|
|
217
|
+
) {
|
|
218
|
+
throw new TypeError(`@solidjs/compiler \`${key}\` option must be boolean`);
|
|
219
|
+
}
|
|
220
|
+
if (key === "bundler" && !refreshBundlers.has(value)) {
|
|
221
|
+
throw new TypeError('@solidjs/compiler `bundler` option must be "vite" or "standard"');
|
|
222
|
+
}
|
|
223
|
+
if (key === "jsx") {
|
|
224
|
+
// The Babel plugin's JSX-granularity mode (its default!) is not
|
|
225
|
+
// ported; only the vite-plugin-solid configuration (`jsx: false`) is.
|
|
226
|
+
if (value !== false) {
|
|
227
|
+
throw new Error(
|
|
228
|
+
"@solidjs/compiler transformRefresh() does not support `jsx: true` yet; pass `jsx: false`"
|
|
229
|
+
);
|
|
230
|
+
}
|
|
231
|
+
nativeOptions.jsx = false;
|
|
232
|
+
continue;
|
|
233
|
+
}
|
|
234
|
+
nativeOptions[key] = value;
|
|
235
|
+
}
|
|
236
|
+
return nativeOptions;
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
const directivesOptionKeys = new Set([
|
|
240
|
+
"filename",
|
|
241
|
+
"root",
|
|
242
|
+
"mode",
|
|
243
|
+
"env",
|
|
244
|
+
"directive",
|
|
245
|
+
"sourceMap",
|
|
246
|
+
"register",
|
|
247
|
+
"create"
|
|
248
|
+
]);
|
|
249
|
+
|
|
250
|
+
function validateDirectivesOptions(options) {
|
|
251
|
+
if (options == null || typeof options !== "object" || Array.isArray(options)) {
|
|
252
|
+
throw new TypeError("@solidjs/compiler transformDirectives() expects options to be an object");
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const nativeOptions = {};
|
|
256
|
+
for (const [key, value] of Object.entries(options)) {
|
|
257
|
+
if (!directivesOptionKeys.has(key)) {
|
|
258
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
259
|
+
}
|
|
260
|
+
if (key === "mode") {
|
|
261
|
+
if (value !== "server" && value !== "client") {
|
|
262
|
+
throw new TypeError('@solidjs/compiler `mode` option must be "server" or "client"');
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
if (key === "env") {
|
|
266
|
+
if (value !== "production" && value !== "development") {
|
|
267
|
+
throw new TypeError('@solidjs/compiler `env` option must be "production" or "development"');
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
if (key === "register" || key === "create") {
|
|
271
|
+
validateImportDefinition(key, value);
|
|
272
|
+
}
|
|
273
|
+
nativeOptions[key] = value;
|
|
274
|
+
}
|
|
275
|
+
if (typeof nativeOptions.filename !== "string") {
|
|
276
|
+
throw new TypeError("@solidjs/compiler transformDirectives() requires `filename`");
|
|
277
|
+
}
|
|
278
|
+
return nativeOptions;
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
function validateImportDefinition(key, value) {
|
|
282
|
+
if (typeof value !== "object" || value == null || Array.isArray(value)) {
|
|
283
|
+
throw new TypeError(`@solidjs/compiler \`${key}\` option must be an object`);
|
|
284
|
+
}
|
|
285
|
+
for (const nested of Object.keys(value)) {
|
|
286
|
+
if (nested !== "kind" && nested !== "name" && nested !== "source") {
|
|
287
|
+
throw new Error(`@solidjs/compiler received unknown \`${key}\` option \`${nested}\``);
|
|
288
|
+
}
|
|
289
|
+
}
|
|
290
|
+
if (typeof value.source !== "string") {
|
|
291
|
+
throw new TypeError(`@solidjs/compiler \`${key}.source\` must be a string`);
|
|
292
|
+
}
|
|
293
|
+
if (value.kind != null && value.kind !== "named" && value.kind !== "default") {
|
|
294
|
+
throw new TypeError(`@solidjs/compiler \`${key}.kind\` must be "named" or "default"`);
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
const nativeOptionKeys = new Set([
|
|
299
|
+
"filename",
|
|
300
|
+
"moduleName",
|
|
301
|
+
"generate",
|
|
302
|
+
"hydratable",
|
|
303
|
+
"dev",
|
|
304
|
+
"sourceNames",
|
|
305
|
+
"sourceMap",
|
|
306
|
+
"contextToCustomElements",
|
|
307
|
+
"delegateEvents",
|
|
308
|
+
"delegatedEvents",
|
|
309
|
+
"omitQuotes",
|
|
310
|
+
"omitAttributeSpacing",
|
|
311
|
+
"inlineStyles",
|
|
312
|
+
"effectWrapper",
|
|
313
|
+
"wrapConditionals",
|
|
314
|
+
"memoWrapper",
|
|
315
|
+
"requireImportSource",
|
|
316
|
+
"validate",
|
|
317
|
+
"staticMarker",
|
|
318
|
+
"omitNestedClosingTags",
|
|
319
|
+
"omitLastClosingTag",
|
|
320
|
+
"serverComponents",
|
|
321
|
+
"hoistProps",
|
|
322
|
+
"builtIns",
|
|
323
|
+
"renderers"
|
|
324
|
+
]);
|
|
325
|
+
|
|
326
|
+
function validateOptions(code, options) {
|
|
327
|
+
if (options == null) return options;
|
|
328
|
+
if (typeof options !== "object" || Array.isArray(options)) {
|
|
329
|
+
throw new TypeError("@solidjs/compiler transform() expects options to be an object");
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
const nativeOptions = {};
|
|
333
|
+
for (const [key, value] of Object.entries(options)) {
|
|
334
|
+
if (key === "effectWrapper" || key === "memoWrapper") {
|
|
335
|
+
if (typeof value !== "string" && typeof value !== "boolean") {
|
|
336
|
+
throw new TypeError(
|
|
337
|
+
`@solidjs/compiler \`${key}\` option must be a string import name or false`
|
|
338
|
+
);
|
|
339
|
+
}
|
|
340
|
+
nativeOptions[key] = value;
|
|
341
|
+
continue;
|
|
342
|
+
}
|
|
343
|
+
if (key === "requireImportSource") {
|
|
344
|
+
if (value !== false && typeof value !== "string") {
|
|
345
|
+
throw new TypeError(
|
|
346
|
+
"@solidjs/compiler `requireImportSource` option must be false or a string"
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (value !== false) nativeOptions.requireImportSource = value;
|
|
350
|
+
continue;
|
|
351
|
+
}
|
|
352
|
+
if (key === "wrapConditionals") {
|
|
353
|
+
if (typeof value !== "boolean") {
|
|
354
|
+
throw new TypeError("@solidjs/compiler `wrapConditionals` option must be boolean");
|
|
355
|
+
}
|
|
356
|
+
nativeOptions.wrapConditionals = value;
|
|
357
|
+
continue;
|
|
358
|
+
}
|
|
359
|
+
if (key === "validate") {
|
|
360
|
+
if (typeof value !== "boolean") {
|
|
361
|
+
throw new TypeError("@solidjs/compiler `validate` option must be boolean");
|
|
362
|
+
}
|
|
363
|
+
nativeOptions.validate = value;
|
|
364
|
+
continue;
|
|
365
|
+
}
|
|
366
|
+
if (key === "sourceNames") {
|
|
367
|
+
validateSourceNames(value);
|
|
368
|
+
nativeOptions.sourceNames = value;
|
|
369
|
+
continue;
|
|
370
|
+
}
|
|
371
|
+
if (nativeOptionKeys.has(key)) {
|
|
372
|
+
if (key === "renderers") validateRenderers(value);
|
|
373
|
+
nativeOptions[key] = value;
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
377
|
+
}
|
|
378
|
+
return nativeOptions;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
const sourceNameKinds = new Set(["components", "bindings"]);
|
|
382
|
+
|
|
383
|
+
function validateSourceNames(value) {
|
|
384
|
+
if (typeof value === "boolean") return;
|
|
385
|
+
if (typeof value !== "object" || value == null || Array.isArray(value)) {
|
|
386
|
+
throw new TypeError("@solidjs/compiler `sourceNames` option must be boolean or an object");
|
|
387
|
+
}
|
|
388
|
+
for (const [kind, enabled] of Object.entries(value)) {
|
|
389
|
+
if (!sourceNameKinds.has(kind)) {
|
|
390
|
+
throw new Error(`@solidjs/compiler received unknown \`sourceNames\` kind \`${kind}\``);
|
|
391
|
+
}
|
|
392
|
+
if (typeof enabled !== "boolean") {
|
|
393
|
+
throw new TypeError(`@solidjs/compiler \`sourceNames.${kind}\` must be boolean`);
|
|
394
|
+
}
|
|
395
|
+
}
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
function validateRenderers(renderers) {
|
|
399
|
+
if (renderers == null) return;
|
|
400
|
+
if (!Array.isArray(renderers)) {
|
|
401
|
+
throw new TypeError("@solidjs/compiler `renderers` option must be an array");
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
for (const renderer of renderers) {
|
|
405
|
+
if (typeof renderer !== "object" || renderer == null || Array.isArray(renderer)) {
|
|
406
|
+
throw new TypeError("@solidjs/compiler renderer entries must be objects");
|
|
407
|
+
}
|
|
408
|
+
for (const key of Object.keys(renderer)) {
|
|
409
|
+
if (key !== "name" && key !== "moduleName" && key !== "elements") {
|
|
410
|
+
throw new Error(`@solidjs/compiler received unknown renderer option \`${key}\``);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
if (renderer.name !== "dom") {
|
|
414
|
+
throw new Error(
|
|
415
|
+
"@solidjs/compiler dynamic renderers only support the `dom` renderer override"
|
|
416
|
+
);
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
function platformArchSuffix() {
|
|
422
|
+
const { platform, arch } = process;
|
|
423
|
+
if (platform === "darwin" && (arch === "x64" || arch === "arm64")) return `darwin-${arch}`;
|
|
424
|
+
if (platform === "linux" && (arch === "x64" || arch === "arm64")) return `linux-${arch}-gnu`;
|
|
425
|
+
if (platform === "win32" && arch === "x64") return "win32-x64-msvc";
|
|
426
|
+
return null;
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
function tryPackage(packageName) {
|
|
430
|
+
try {
|
|
431
|
+
return { binding: require(packageName) };
|
|
432
|
+
} catch (error) {
|
|
433
|
+
if (isMissingPackage(error, packageName)) return { missing: true };
|
|
434
|
+
return { error };
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
function requireBinding() {
|
|
439
|
+
const explicit = process.env.SOLID_COMPILER_NATIVE;
|
|
440
|
+
if (explicit) return require(explicit);
|
|
441
|
+
|
|
442
|
+
const forceWasi = process.env.NAPI_RS_FORCE_WASI;
|
|
443
|
+
if (forceWasi === "true" || forceWasi === "error") {
|
|
444
|
+
const wasi = requireWasi();
|
|
445
|
+
if (wasi) return wasi;
|
|
446
|
+
if (forceWasi === "error") {
|
|
447
|
+
throw new Error("WASI binding not found and NAPI_RS_FORCE_WASI is set to error");
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
let nativeError;
|
|
452
|
+
const suffix = platformArchSuffix();
|
|
453
|
+
|
|
454
|
+
// Local builds first: the published package ships no local binary (see
|
|
455
|
+
// "files"), so a `compiler.node` next to this file can only be a
|
|
456
|
+
// development build — which must shadow the installed platform package,
|
|
457
|
+
// or the monorepo's own tests silently run against the last release.
|
|
458
|
+
const localCandidates = [];
|
|
459
|
+
if (suffix) localCandidates.push(`compiler.${suffix}.node`);
|
|
460
|
+
localCandidates.push("compiler.node");
|
|
461
|
+
for (const file of localCandidates) {
|
|
462
|
+
const full = path.join(__dirname, file);
|
|
463
|
+
if (fs.existsSync(full)) {
|
|
464
|
+
try {
|
|
465
|
+
return require(full);
|
|
466
|
+
} catch (error) {
|
|
467
|
+
// A present-but-unloadable dev build is an error, not a fallback:
|
|
468
|
+
// degrading to the published binary would silently test stale code.
|
|
469
|
+
error.message += `\nA local development build (${file}) exists but could not be loaded. Rebuild with \`pnpm run build:debug\` or delete it to use the installed @solidjs/compiler-* package.`;
|
|
470
|
+
throw error;
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
|
|
475
|
+
if (suffix) {
|
|
476
|
+
const next = tryPackage(`@solidjs/compiler-${suffix}`);
|
|
477
|
+
if (next.binding) return next.binding;
|
|
478
|
+
if (next.error) nativeError = next.error;
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
const wasi = requireWasi();
|
|
482
|
+
if (wasi) return wasi;
|
|
483
|
+
|
|
484
|
+
if (nativeError) {
|
|
485
|
+
nativeError.message +=
|
|
486
|
+
"\nThe native binding could not be loaded and the optional " +
|
|
487
|
+
"@solidjs/compiler-wasm32-wasi fallback is not installed.";
|
|
488
|
+
throw nativeError;
|
|
489
|
+
}
|
|
490
|
+
|
|
491
|
+
throw new Error(
|
|
492
|
+
`Could not find an @solidjs/compiler binding for ${process.platform}-${process.arch}` +
|
|
493
|
+
(suffix
|
|
494
|
+
? ` (expected @solidjs/compiler-${suffix}, @solidjs/compiler-wasm32-wasi, or a local build)`
|
|
495
|
+
: " (no prebuilt binary is published for this platform)") +
|
|
496
|
+
". Install with WASM support or run `pnpm run build` in packages/compiler."
|
|
497
|
+
);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function requireWasi() {
|
|
501
|
+
const next = tryPackage("@solidjs/compiler-wasm32-wasi");
|
|
502
|
+
if (next.binding) return next.binding;
|
|
503
|
+
if (next.error) throw next.error;
|
|
504
|
+
|
|
505
|
+
const localWasi = path.join(__dirname, "compiler.wasi.cjs");
|
|
506
|
+
if (fs.existsSync(localWasi)) return require(localWasi);
|
|
507
|
+
return null;
|
|
508
|
+
}
|
|
509
|
+
|
|
510
|
+
function isMissingPackage(error, packageName) {
|
|
511
|
+
return (
|
|
512
|
+
error &&
|
|
513
|
+
error.code === "MODULE_NOT_FOUND" &&
|
|
514
|
+
typeof error.message === "string" &&
|
|
515
|
+
error.message.includes(`'${packageName}'`)
|
|
516
|
+
);
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
module.exports = {
|
|
520
|
+
transform,
|
|
521
|
+
transformAsync,
|
|
522
|
+
projectTsrxForTypecheck,
|
|
523
|
+
transformDirectives,
|
|
524
|
+
transformDirectivesAsync,
|
|
525
|
+
transformLazy,
|
|
526
|
+
transformLazyAsync,
|
|
527
|
+
transformRefresh,
|
|
528
|
+
transformRefreshAsync,
|
|
529
|
+
transformSourceNames,
|
|
530
|
+
transformSourceNamesAsync
|
|
531
|
+
};
|
package/package.json
ADDED
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@solidjs/compiler",
|
|
3
|
+
"description": "Solid's native Oxc JSX compiler",
|
|
4
|
+
"version": "2.0.0-rc.10",
|
|
5
|
+
"author": "Ryan Carniato",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "git+https://github.com/solidjs/solid.git",
|
|
10
|
+
"directory": "packages/compiler"
|
|
11
|
+
},
|
|
12
|
+
"main": "index.js",
|
|
13
|
+
"types": "types.d.ts",
|
|
14
|
+
"sideEffects": false,
|
|
15
|
+
"exports": {
|
|
16
|
+
".": {
|
|
17
|
+
"types": "./types.d.ts",
|
|
18
|
+
"require": "./index.js",
|
|
19
|
+
"default": "./index.js"
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"files": [
|
|
23
|
+
"index.js",
|
|
24
|
+
"types.d.ts",
|
|
25
|
+
"README.md"
|
|
26
|
+
],
|
|
27
|
+
"scripts": {
|
|
28
|
+
"build": "napi build --release --strip --manifest-path ./Cargo.toml",
|
|
29
|
+
"build:debug": "napi build --manifest-path ./Cargo.toml",
|
|
30
|
+
"bench": "pnpm run build && node scripts/bench.mjs",
|
|
31
|
+
"lint": "cargo clippy --manifest-path ./Cargo.toml -- -D warnings",
|
|
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 && cargo test --manifest-path ./Cargo.toml --no-default-features --features tsrx",
|
|
34
|
+
"artifacts": "napi artifacts",
|
|
35
|
+
"napi:version": "napi version && node ./sync-optional-deps.mjs",
|
|
36
|
+
"create-npm-dirs": "napi create-npm-dirs"
|
|
37
|
+
},
|
|
38
|
+
"napi": {
|
|
39
|
+
"binaryName": "compiler",
|
|
40
|
+
"targets": [
|
|
41
|
+
"x86_64-apple-darwin",
|
|
42
|
+
"aarch64-apple-darwin",
|
|
43
|
+
"x86_64-unknown-linux-gnu",
|
|
44
|
+
"aarch64-unknown-linux-gnu",
|
|
45
|
+
"x86_64-pc-windows-msvc",
|
|
46
|
+
"wasm32-wasip1-threads"
|
|
47
|
+
],
|
|
48
|
+
"wasm": {
|
|
49
|
+
"browser": {
|
|
50
|
+
"fs": false,
|
|
51
|
+
"asyncInit": false,
|
|
52
|
+
"buffer": false,
|
|
53
|
+
"errorEvent": true
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
},
|
|
57
|
+
"devDependencies": {
|
|
58
|
+
"@emnapi/core": "^1.11.3",
|
|
59
|
+
"@emnapi/runtime": "^1.11.3",
|
|
60
|
+
"@napi-rs/cli": "^3.7.2",
|
|
61
|
+
"@napi-rs/wasm-runtime": "^1.1.6",
|
|
62
|
+
"emnapi": "^1.11.3"
|
|
63
|
+
},
|
|
64
|
+
"optionalDependencies": {
|
|
65
|
+
"@solidjs/compiler-darwin-x64": "2.0.0-rc.10",
|
|
66
|
+
"@solidjs/compiler-darwin-arm64": "2.0.0-rc.10",
|
|
67
|
+
"@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.10",
|
|
68
|
+
"@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.10",
|
|
69
|
+
"@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.10",
|
|
70
|
+
"@solidjs/compiler-wasm32-wasi": "2.0.0-rc.10"
|
|
71
|
+
}
|
|
72
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
/** The object form of `sourceNames`: which kinds of source name to carry into output. */
|
|
2
|
+
export interface SourceNamesOptions {
|
|
3
|
+
/** Component owner labels: the tag as written, as `createComponent`'s third argument. */
|
|
4
|
+
components?: boolean;
|
|
5
|
+
/** Binding effect labels: the element and attribute (or hole) each compiled effect writes. */
|
|
6
|
+
bindings?: boolean;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export interface TransformOptions {
|
|
10
|
+
filename?: string;
|
|
11
|
+
/** Default `"@solidjs/web"`. */
|
|
12
|
+
moduleName?: string;
|
|
13
|
+
/**
|
|
14
|
+
* Source syntax frontend, matching `@solidjs/babel-plugin`: `"auto"`
|
|
15
|
+
* (default) routes `.tsrx` filenames through the TSRX frontend and
|
|
16
|
+
* everything else through standard JSX; `"tsrx"` and `"jsx"` force a
|
|
17
|
+
* frontend regardless of filename. TSRX support is experimental.
|
|
18
|
+
*/
|
|
19
|
+
syntax?: "auto" | "jsx" | "tsrx";
|
|
20
|
+
generate?: "dom" | "ssr" | "universal" | "dynamic";
|
|
21
|
+
hydratable?: boolean;
|
|
22
|
+
dev?: boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Names as written in source, carried into output so the dev and observe
|
|
25
|
+
* runtimes can label the reactive graph after minification.
|
|
26
|
+
* `components`: the tag as a third `createComponent` argument
|
|
27
|
+
* (`createComponent(Home, props, "Home")`) — DOM and SSR output (SSR
|
|
28
|
+
* keeps the `createComponent` call it would otherwise inline to
|
|
29
|
+
* `Comp(props)`); not universal or dynamic. `bindings`: every compiled
|
|
30
|
+
* binding effect named by what it writes — `span.textContent`,
|
|
31
|
+
* `div.class:active`, a hole `div.children`, a spread `div.spread` — as
|
|
32
|
+
* an options argument on `effect`/`insert`/`spread`; DOM output only.
|
|
33
|
+
* The production runtimes ignore the names. Defaults to `dev`: unset,
|
|
34
|
+
* every kind is on in dev and off otherwise; `true`/`false` sets every
|
|
35
|
+
* kind; an object picks, and each kind it leaves unspecified follows
|
|
36
|
+
* `dev`. Primitive names (`createSignal(0, { name: "count" })`) are not a
|
|
37
|
+
* kind here — they come from the standalone `transformSourceNames` pass,
|
|
38
|
+
* which the build tool runs on every module independently of this
|
|
39
|
+
* transform.
|
|
40
|
+
*/
|
|
41
|
+
sourceNames?: boolean | SourceNamesOptions;
|
|
42
|
+
sourceMap?: boolean;
|
|
43
|
+
contextToCustomElements?: boolean;
|
|
44
|
+
delegateEvents?: boolean;
|
|
45
|
+
delegatedEvents?: string[];
|
|
46
|
+
omitQuotes?: boolean;
|
|
47
|
+
omitAttributeSpacing?: boolean;
|
|
48
|
+
inlineStyles?: boolean;
|
|
49
|
+
effectWrapper?: "effect" | false;
|
|
50
|
+
wrapConditionals?: boolean;
|
|
51
|
+
memoWrapper?: "memo" | false;
|
|
52
|
+
staticMarker?: string;
|
|
53
|
+
validate?: boolean;
|
|
54
|
+
omitNestedClosingTags?: boolean;
|
|
55
|
+
omitLastClosingTag?: boolean;
|
|
56
|
+
serverComponents?: boolean;
|
|
57
|
+
/** SSR-only, default `true`: component props literals with getters compile to module-level constructors with shared getters (#3511). */
|
|
58
|
+
hoistProps?: boolean;
|
|
59
|
+
/** Default `["For", "Show", "Switch", "Match", "Loading", "Reveal", "Portal", "Repeat", "Dynamic", "Errored"]`. */
|
|
60
|
+
builtIns?: string[];
|
|
61
|
+
requireImportSource?: false | string;
|
|
62
|
+
renderers?: RendererOption[];
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
export interface RendererOption {
|
|
66
|
+
name: string;
|
|
67
|
+
moduleName?: string;
|
|
68
|
+
elements: string[];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export interface TransformResult {
|
|
72
|
+
code: string;
|
|
73
|
+
map?: string | null;
|
|
74
|
+
/** Extracted scoped CSS for TSRX sources. */
|
|
75
|
+
css?: string | null;
|
|
76
|
+
/** Space-separated TSRX scope hashes. */
|
|
77
|
+
cssHash?: string | null;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function transform(code: string, options?: TransformOptions | null): TransformResult;
|
|
81
|
+
export function transformAsync(
|
|
82
|
+
code: string,
|
|
83
|
+
options?: TransformOptions | null
|
|
84
|
+
): Promise<TransformResult>;
|
|
85
|
+
|
|
86
|
+
export interface ProjectTsrxForTypecheckOptions {
|
|
87
|
+
filename?: string;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export interface TsrxTypecheckEmbeddedRegion {
|
|
91
|
+
kind: "css" | "script";
|
|
92
|
+
/** Authored JavaScript string offset in UTF-16 code units. */
|
|
93
|
+
start: number;
|
|
94
|
+
/** Authored JavaScript string offset in UTF-16 code units. */
|
|
95
|
+
end: number;
|
|
96
|
+
content: string;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface TsrxTypecheckMapping {
|
|
100
|
+
/** Authored JavaScript string offset in UTF-16 code units. */
|
|
101
|
+
sourceStart: number;
|
|
102
|
+
/** Generated JavaScript string offset in UTF-16 code units. */
|
|
103
|
+
generatedStart: number;
|
|
104
|
+
sourceLength: number;
|
|
105
|
+
generatedLength: number;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export interface TsrxTypecheckProjectionResult {
|
|
109
|
+
/** Valid post-semantic-rewrite TypeScript/TSX. */
|
|
110
|
+
code: string;
|
|
111
|
+
/** JSON source map from virtual TSX back to the authored `.tsrx` source. */
|
|
112
|
+
map: string;
|
|
113
|
+
/** Exact equal-text ranges suitable for editor feature mappings. */
|
|
114
|
+
mappings: TsrxTypecheckMapping[];
|
|
115
|
+
css: string;
|
|
116
|
+
cssHash: string | null;
|
|
117
|
+
embeddedRegions: TsrxTypecheckEmbeddedRegion[];
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Experimental compiler-owned TSRX projection for typechecking and editor
|
|
122
|
+
* tooling. This API is host-independent and does not run a runtime renderer.
|
|
123
|
+
*/
|
|
124
|
+
export function projectTsrxForTypecheck(
|
|
125
|
+
code: string,
|
|
126
|
+
options?: ProjectTsrxForTypecheckOptions | null
|
|
127
|
+
): TsrxTypecheckProjectionResult;
|
|
128
|
+
|
|
129
|
+
export interface DirectiveImportDefinition {
|
|
130
|
+
kind?: "named" | "default";
|
|
131
|
+
name?: string;
|
|
132
|
+
source: string;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Options for the experimental `"use server"` directive pass. Applies to
|
|
137
|
+
* plain `.js`/`.ts` modules as well as JSX/TSX.
|
|
138
|
+
*/
|
|
139
|
+
export interface TransformDirectivesOptions {
|
|
140
|
+
/** Required — function IDs hash the root-relative file path. */
|
|
141
|
+
filename: string;
|
|
142
|
+
/** Project root for ID hashing. Defaults to the working directory. */
|
|
143
|
+
root?: string;
|
|
144
|
+
/**
|
|
145
|
+
* `"server"` keeps the module and registers extracted functions;
|
|
146
|
+
* `"client"` replaces them with reference proxies and strips server-only
|
|
147
|
+
* code.
|
|
148
|
+
*/
|
|
149
|
+
mode: "server" | "client";
|
|
150
|
+
/** `"development"` appends function names to generated IDs. */
|
|
151
|
+
env?: "production" | "development";
|
|
152
|
+
/** @default "use server" */
|
|
153
|
+
directive?: string;
|
|
154
|
+
sourceMap?: boolean;
|
|
155
|
+
/** Runtime import for `registerServerReference` (server output). */
|
|
156
|
+
register?: DirectiveImportDefinition;
|
|
157
|
+
/** Runtime import for `createServerReference` (both outputs). */
|
|
158
|
+
create?: DirectiveImportDefinition;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** One extracted server function, for building a bundler manifest. */
|
|
162
|
+
export interface ServerFunctionMeta {
|
|
163
|
+
/** The wire ID (`<name>-<hash>[-<ordinal>]`). */
|
|
164
|
+
id: string;
|
|
165
|
+
/**
|
|
166
|
+
* The dotted binding path that names the function, such as
|
|
167
|
+
* `handlers.save`. `anonymous` when no enclosing binding applies.
|
|
168
|
+
*/
|
|
169
|
+
name: string;
|
|
170
|
+
/** Export names bound to this function (module-level directives only). */
|
|
171
|
+
exports: string[];
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
export interface TransformDirectivesResult {
|
|
175
|
+
code: string;
|
|
176
|
+
map?: string | null;
|
|
177
|
+
/** False when the module contained no matching directive. */
|
|
178
|
+
valid: boolean;
|
|
179
|
+
functions: ServerFunctionMeta[];
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export function transformDirectives(
|
|
183
|
+
code: string,
|
|
184
|
+
options: TransformDirectivesOptions
|
|
185
|
+
): TransformDirectivesResult;
|
|
186
|
+
export function transformDirectivesAsync(
|
|
187
|
+
code: string,
|
|
188
|
+
options: TransformDirectivesOptions
|
|
189
|
+
): Promise<TransformDirectivesResult>;
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Options for the experimental `lazy()` module-URL pass (ported from
|
|
193
|
+
* vite-plugin-solid's `lazy-module-url` Babel plugin).
|
|
194
|
+
*/
|
|
195
|
+
export interface TransformLazyOptions {
|
|
196
|
+
/**
|
|
197
|
+
* Mirrors the Babel plugin: without a filename the pass is a no-op (the
|
|
198
|
+
* emitted placeholder is only useful to a bundler resolving relative to a
|
|
199
|
+
* module id).
|
|
200
|
+
*/
|
|
201
|
+
filename?: string;
|
|
202
|
+
sourceMap?: boolean;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export function transformLazy(code: string, options?: TransformLazyOptions | null): TransformResult;
|
|
206
|
+
export function transformLazyAsync(
|
|
207
|
+
code: string,
|
|
208
|
+
options?: TransformLazyOptions | null
|
|
209
|
+
): Promise<TransformResult>;
|
|
210
|
+
|
|
211
|
+
/**
|
|
212
|
+
* Options for the experimental solid-refresh HMR pass (ported from the
|
|
213
|
+
* `solid-refresh` Babel plugin, `jsx: false` mode). Dev-only.
|
|
214
|
+
*/
|
|
215
|
+
export interface TransformRefreshOptions {
|
|
216
|
+
/**
|
|
217
|
+
* Used for `location` metadata (cwd-relative, matching the Babel plugin)
|
|
218
|
+
* and to pick the parser dialect. Without it no locations are emitted.
|
|
219
|
+
*/
|
|
220
|
+
filename?: string;
|
|
221
|
+
/**
|
|
222
|
+
* Selects the HMR API — the runtime modes `solid-js/refresh` knows:
|
|
223
|
+
* `import.meta.hot` (vite), or `module.hot` / `import.meta.webpackHot`
|
|
224
|
+
* (standard: webpack, Rspack — the runtime probes for whichever exists).
|
|
225
|
+
* @default "standard"
|
|
226
|
+
*/
|
|
227
|
+
bundler?: "vite" | "standard";
|
|
228
|
+
/**
|
|
229
|
+
* Wrap top-level `render()`/`hydrate()` calls (imported from
|
|
230
|
+
* `@solidjs/web`) with `hot.dispose` cleanup.
|
|
231
|
+
* @default true
|
|
232
|
+
*/
|
|
233
|
+
fixRender?: boolean;
|
|
234
|
+
/**
|
|
235
|
+
* Emit per-component `signature`/`dependencies` metadata for granular HMR.
|
|
236
|
+
* @default true
|
|
237
|
+
*/
|
|
238
|
+
granular?: boolean;
|
|
239
|
+
/**
|
|
240
|
+
* The Babel plugin's JSX-granularity mode is not ported; only `false` is
|
|
241
|
+
* accepted (what vite-plugin-solid passes).
|
|
242
|
+
*/
|
|
243
|
+
jsx?: false;
|
|
244
|
+
/**
|
|
245
|
+
* Module the runtime helpers (`$$registry`, `$$component`, `$$refresh`,
|
|
246
|
+
* `$$decline`) are imported from. The dev-only `solid-js/refresh` entry
|
|
247
|
+
* exposes the same frozen ABI.
|
|
248
|
+
* @default "solid-refresh"
|
|
249
|
+
*/
|
|
250
|
+
importSource?: string;
|
|
251
|
+
sourceMap?: boolean;
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
export function transformRefresh(
|
|
255
|
+
code: string,
|
|
256
|
+
options?: TransformRefreshOptions | null
|
|
257
|
+
): TransformResult;
|
|
258
|
+
export function transformRefreshAsync(
|
|
259
|
+
code: string,
|
|
260
|
+
options?: TransformRefreshOptions | null
|
|
261
|
+
): Promise<TransformResult>;
|
|
262
|
+
|
|
263
|
+
/**
|
|
264
|
+
* Options for the `sourceNames.primitives` pass: reactive primitives named
|
|
265
|
+
* after the identifier they are declared as. `const [count, setCount] =
|
|
266
|
+
* createSignal(0)` becomes `createSignal(0, { name: "count" })`; inside a
|
|
267
|
+
* non-component function the name is prefixed with that function's
|
|
268
|
+
* (`createCounter.count`). Only calls resolving to imports from `solid-js` /
|
|
269
|
+
* `@solidjs/signals` are named, and an explicit `name` is never overridden.
|
|
270
|
+
* Plain JavaScript in and out, so it applies to `.ts`/`.js` modules too.
|
|
271
|
+
* This standalone pass is the single owner of primitive naming: the build
|
|
272
|
+
* tool (`@solidjs/vite-plugin`) runs it on every module — `.ts`, `.js` and
|
|
273
|
+
* JSX files alike — independently of which JSX compiler (this one or
|
|
274
|
+
* `@solidjs/babel-plugin`) handles the file's JSX. `transform()`'s
|
|
275
|
+
* `sourceNames` option covers only the JSX-level kinds.
|
|
276
|
+
*/
|
|
277
|
+
export interface TransformSourceNamesOptions {
|
|
278
|
+
/** Picks the parser dialect (`.ts`, `.tsx`, `.js`, `.jsx`); TSX without one. */
|
|
279
|
+
filename?: string;
|
|
280
|
+
sourceMap?: boolean;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
export function transformSourceNames(
|
|
284
|
+
code: string,
|
|
285
|
+
options?: TransformSourceNamesOptions | null
|
|
286
|
+
): TransformResult;
|
|
287
|
+
export function transformSourceNamesAsync(
|
|
288
|
+
code: string,
|
|
289
|
+
options?: TransformSourceNamesOptions | null
|
|
290
|
+
): Promise<TransformResult>;
|