@solidjs/compiler 2.0.0-rc.2
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 +162 -0
- package/index.js +414 -0
- package/package.json +72 -0
- package/types.d.ts +174 -0
package/README.md
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
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
|
+
### Source maps
|
|
82
|
+
|
|
83
|
+
Pass `sourceMap: true` to receive a JSON source map string in `result.map`.
|
|
84
|
+
|
|
85
|
+
### Options
|
|
86
|
+
|
|
87
|
+
- `filename`
|
|
88
|
+
- `moduleName` (default `"@solidjs/web"`)
|
|
89
|
+
- `generate`: `"dom"`, `"ssr"`, `"universal"`, or `"dynamic"` (default `"dom"`)
|
|
90
|
+
- `hydratable`
|
|
91
|
+
- `dev`
|
|
92
|
+
- `sourceMap`
|
|
93
|
+
- `contextToCustomElements` (default `true`)
|
|
94
|
+
- `delegateEvents`
|
|
95
|
+
- `delegatedEvents`
|
|
96
|
+
- `omitQuotes`
|
|
97
|
+
- `omitAttributeSpacing`
|
|
98
|
+
- `inlineStyles`
|
|
99
|
+
- `effectWrapper`: import name string, or `false` to disable
|
|
100
|
+
- `memoWrapper`: import name string, or `false` to disable
|
|
101
|
+
- `wrapConditionals`
|
|
102
|
+
- `staticMarker`
|
|
103
|
+
- `validate`
|
|
104
|
+
- `omitNestedClosingTags`
|
|
105
|
+
- `omitLastClosingTag`
|
|
106
|
+
- `builtIns` (default `["For", "Show", "Switch", "Match", "Loading", "Reveal", "Portal", "Repeat", "Dynamic", "Errored"]`)
|
|
107
|
+
- `requireImportSource`
|
|
108
|
+
- `serverComponents`
|
|
109
|
+
- `renderers`
|
|
110
|
+
|
|
111
|
+
### Server function directives (experimental)
|
|
112
|
+
|
|
113
|
+
`transformDirectives(code, options)` is a second pass for `"use server"`. It applies to plain `.js`/`.ts` as well as JSX/TSX.
|
|
114
|
+
|
|
115
|
+
```js
|
|
116
|
+
const { transformDirectives } = require("@solidjs/compiler");
|
|
117
|
+
|
|
118
|
+
const result = transformDirectives(source, {
|
|
119
|
+
filename: "/project/src/api.ts",
|
|
120
|
+
root: "/project",
|
|
121
|
+
mode: "server" // or "client"
|
|
122
|
+
});
|
|
123
|
+
|
|
124
|
+
result.valid; // false when no directive matched — keep the original module
|
|
125
|
+
result.code;
|
|
126
|
+
result.functions; // [{ id, name, exports }] for manifest building
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
The runtime module defaults to `@solidjs/web/server-functions`. Function IDs use `xxhash32(root-relative path)-<count>` (name-suffixed with `env: "development"`). There are also experimental `transformLazy` and `transformRefresh` passes.
|
|
130
|
+
|
|
131
|
+
## Rust compiler core
|
|
132
|
+
|
|
133
|
+
The crate also exposes a host-independent Rust API. The crate name is `solidjs-compiler`; the Node `transform()` delegates to the same core.
|
|
134
|
+
|
|
135
|
+
```rust
|
|
136
|
+
use solidjs_compiler::{compile, CompileOptions};
|
|
137
|
+
|
|
138
|
+
let output = compile(
|
|
139
|
+
"const view = <div>{name()}</div>;",
|
|
140
|
+
&CompileOptions::default(),
|
|
141
|
+
)?;
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
`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
|
+
|
|
146
|
+
> **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
|
+
|
|
148
|
+
## Performance
|
|
149
|
+
|
|
150
|
+
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):
|
|
151
|
+
|
|
152
|
+
| Workload | babel-plugin | compiler | Speedup |
|
|
153
|
+
| ----------------------------------------------- | -----------: | -------: | ------: |
|
|
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 |
|
|
157
|
+
|
|
158
|
+
Native throughput stays roughly flat as input grows, while Babel's per-file cost grows super-linearly.
|
|
159
|
+
|
|
160
|
+
## Architecture
|
|
161
|
+
|
|
162
|
+
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,414 @@
|
|
|
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
|
+
return {
|
|
16
|
+
code: result.code,
|
|
17
|
+
map: result.map ?? null
|
|
18
|
+
};
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function transformAsync(code, options) {
|
|
22
|
+
return Promise.resolve().then(() => transform(code, options));
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function transformDirectives(code, options) {
|
|
26
|
+
if (typeof code !== "string") {
|
|
27
|
+
throw new TypeError("@solidjs/compiler transformDirectives() expects source code as a string");
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const nativeOptions = validateDirectivesOptions(options);
|
|
31
|
+
const result = native.transformDirectives(code, nativeOptions);
|
|
32
|
+
return {
|
|
33
|
+
code: result.code,
|
|
34
|
+
map: result.map ?? null,
|
|
35
|
+
valid: result.valid,
|
|
36
|
+
functions: result.functions
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function transformDirectivesAsync(code, options) {
|
|
41
|
+
return Promise.resolve().then(() => transformDirectives(code, options));
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function transformLazy(code, options) {
|
|
45
|
+
if (typeof code !== "string") {
|
|
46
|
+
throw new TypeError("@solidjs/compiler transformLazy() expects source code as a string");
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const nativeOptions = validateLazyOptions(options);
|
|
50
|
+
const result = native.transformLazy(code, nativeOptions);
|
|
51
|
+
return {
|
|
52
|
+
code: result.code,
|
|
53
|
+
map: result.map ?? null
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
function transformLazyAsync(code, options) {
|
|
58
|
+
return Promise.resolve().then(() => transformLazy(code, options));
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function transformRefresh(code, options) {
|
|
62
|
+
if (typeof code !== "string") {
|
|
63
|
+
throw new TypeError("@solidjs/compiler transformRefresh() expects source code as a string");
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const nativeOptions = validateRefreshOptions(options);
|
|
67
|
+
const result = native.transformRefresh(code, nativeOptions);
|
|
68
|
+
return {
|
|
69
|
+
code: result.code,
|
|
70
|
+
map: result.map ?? null
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function transformRefreshAsync(code, options) {
|
|
75
|
+
return Promise.resolve().then(() => transformRefresh(code, options));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const lazyOptionKeys = new Set(["filename", "sourceMap"]);
|
|
79
|
+
|
|
80
|
+
function validateLazyOptions(options) {
|
|
81
|
+
if (options == null) return options;
|
|
82
|
+
if (typeof options !== "object" || Array.isArray(options)) {
|
|
83
|
+
throw new TypeError("@solidjs/compiler transformLazy() expects options to be an object");
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const nativeOptions = {};
|
|
87
|
+
for (const [key, value] of Object.entries(options)) {
|
|
88
|
+
if (!lazyOptionKeys.has(key)) {
|
|
89
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
90
|
+
}
|
|
91
|
+
if (key === "filename" && typeof value !== "string") {
|
|
92
|
+
throw new TypeError("@solidjs/compiler `filename` option must be a string");
|
|
93
|
+
}
|
|
94
|
+
if (key === "sourceMap" && typeof value !== "boolean") {
|
|
95
|
+
throw new TypeError("@solidjs/compiler `sourceMap` option must be boolean");
|
|
96
|
+
}
|
|
97
|
+
nativeOptions[key] = value;
|
|
98
|
+
}
|
|
99
|
+
return nativeOptions;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const refreshOptionKeys = new Set([
|
|
103
|
+
"filename",
|
|
104
|
+
"bundler",
|
|
105
|
+
"fixRender",
|
|
106
|
+
"granular",
|
|
107
|
+
"jsx",
|
|
108
|
+
"importSource",
|
|
109
|
+
"sourceMap"
|
|
110
|
+
]);
|
|
111
|
+
|
|
112
|
+
const refreshBundlers = new Set(["esm", "vite", "webpack5", "rspack-esm", "standard"]);
|
|
113
|
+
|
|
114
|
+
function validateRefreshOptions(options) {
|
|
115
|
+
if (options == null) return options;
|
|
116
|
+
if (typeof options !== "object" || Array.isArray(options)) {
|
|
117
|
+
throw new TypeError("@solidjs/compiler transformRefresh() expects options to be an object");
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
const nativeOptions = {};
|
|
121
|
+
for (const [key, value] of Object.entries(options)) {
|
|
122
|
+
if (!refreshOptionKeys.has(key)) {
|
|
123
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
124
|
+
}
|
|
125
|
+
if ((key === "filename" || key === "importSource") && typeof value !== "string") {
|
|
126
|
+
throw new TypeError(`@solidjs/compiler \`${key}\` option must be a string`);
|
|
127
|
+
}
|
|
128
|
+
if (
|
|
129
|
+
(key === "fixRender" || key === "granular" || key === "sourceMap") &&
|
|
130
|
+
typeof value !== "boolean"
|
|
131
|
+
) {
|
|
132
|
+
throw new TypeError(`@solidjs/compiler \`${key}\` option must be boolean`);
|
|
133
|
+
}
|
|
134
|
+
if (key === "bundler" && !refreshBundlers.has(value)) {
|
|
135
|
+
throw new TypeError(
|
|
136
|
+
'@solidjs/compiler `bundler` option must be "esm", "vite", "webpack5", "rspack-esm" or "standard"'
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
if (key === "jsx") {
|
|
140
|
+
// The Babel plugin's JSX-granularity mode (its default!) is not
|
|
141
|
+
// ported; only the vite-plugin-solid configuration (`jsx: false`) is.
|
|
142
|
+
if (value !== false) {
|
|
143
|
+
throw new Error(
|
|
144
|
+
"@solidjs/compiler transformRefresh() does not support `jsx: true` yet; pass `jsx: false`"
|
|
145
|
+
);
|
|
146
|
+
}
|
|
147
|
+
nativeOptions.jsx = false;
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
nativeOptions[key] = value;
|
|
151
|
+
}
|
|
152
|
+
return nativeOptions;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const directivesOptionKeys = new Set([
|
|
156
|
+
"filename",
|
|
157
|
+
"root",
|
|
158
|
+
"mode",
|
|
159
|
+
"env",
|
|
160
|
+
"directive",
|
|
161
|
+
"sourceMap",
|
|
162
|
+
"register",
|
|
163
|
+
"create"
|
|
164
|
+
]);
|
|
165
|
+
|
|
166
|
+
function validateDirectivesOptions(options) {
|
|
167
|
+
if (options == null || typeof options !== "object" || Array.isArray(options)) {
|
|
168
|
+
throw new TypeError("@solidjs/compiler transformDirectives() expects options to be an object");
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const nativeOptions = {};
|
|
172
|
+
for (const [key, value] of Object.entries(options)) {
|
|
173
|
+
if (!directivesOptionKeys.has(key)) {
|
|
174
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
175
|
+
}
|
|
176
|
+
if (key === "mode") {
|
|
177
|
+
if (value !== "server" && value !== "client") {
|
|
178
|
+
throw new TypeError('@solidjs/compiler `mode` option must be "server" or "client"');
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
if (key === "env") {
|
|
182
|
+
if (value !== "production" && value !== "development") {
|
|
183
|
+
throw new TypeError('@solidjs/compiler `env` option must be "production" or "development"');
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
if (key === "register" || key === "create") {
|
|
187
|
+
validateImportDefinition(key, value);
|
|
188
|
+
}
|
|
189
|
+
nativeOptions[key] = value;
|
|
190
|
+
}
|
|
191
|
+
if (typeof nativeOptions.filename !== "string") {
|
|
192
|
+
throw new TypeError("@solidjs/compiler transformDirectives() requires `filename`");
|
|
193
|
+
}
|
|
194
|
+
return nativeOptions;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
function validateImportDefinition(key, value) {
|
|
198
|
+
if (typeof value !== "object" || value == null || Array.isArray(value)) {
|
|
199
|
+
throw new TypeError(`@solidjs/compiler \`${key}\` option must be an object`);
|
|
200
|
+
}
|
|
201
|
+
for (const nested of Object.keys(value)) {
|
|
202
|
+
if (nested !== "kind" && nested !== "name" && nested !== "source") {
|
|
203
|
+
throw new Error(`@solidjs/compiler received unknown \`${key}\` option \`${nested}\``);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
if (typeof value.source !== "string") {
|
|
207
|
+
throw new TypeError(`@solidjs/compiler \`${key}.source\` must be a string`);
|
|
208
|
+
}
|
|
209
|
+
if (value.kind != null && value.kind !== "named" && value.kind !== "default") {
|
|
210
|
+
throw new TypeError(`@solidjs/compiler \`${key}.kind\` must be "named" or "default"`);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const nativeOptionKeys = new Set([
|
|
215
|
+
"filename",
|
|
216
|
+
"moduleName",
|
|
217
|
+
"generate",
|
|
218
|
+
"hydratable",
|
|
219
|
+
"dev",
|
|
220
|
+
"sourceMap",
|
|
221
|
+
"contextToCustomElements",
|
|
222
|
+
"delegateEvents",
|
|
223
|
+
"delegatedEvents",
|
|
224
|
+
"omitQuotes",
|
|
225
|
+
"omitAttributeSpacing",
|
|
226
|
+
"inlineStyles",
|
|
227
|
+
"effectWrapper",
|
|
228
|
+
"wrapConditionals",
|
|
229
|
+
"memoWrapper",
|
|
230
|
+
"requireImportSource",
|
|
231
|
+
"validate",
|
|
232
|
+
"staticMarker",
|
|
233
|
+
"omitNestedClosingTags",
|
|
234
|
+
"omitLastClosingTag",
|
|
235
|
+
"serverComponents",
|
|
236
|
+
"builtIns",
|
|
237
|
+
"renderers"
|
|
238
|
+
]);
|
|
239
|
+
|
|
240
|
+
function validateOptions(code, options) {
|
|
241
|
+
if (options == null) return options;
|
|
242
|
+
if (typeof options !== "object" || Array.isArray(options)) {
|
|
243
|
+
throw new TypeError("@solidjs/compiler transform() expects options to be an object");
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const nativeOptions = {};
|
|
247
|
+
for (const [key, value] of Object.entries(options)) {
|
|
248
|
+
if (key === "effectWrapper" || key === "memoWrapper") {
|
|
249
|
+
if (typeof value !== "string" && typeof value !== "boolean") {
|
|
250
|
+
throw new TypeError(
|
|
251
|
+
`@solidjs/compiler \`${key}\` option must be a string import name or false`
|
|
252
|
+
);
|
|
253
|
+
}
|
|
254
|
+
nativeOptions[key] = value;
|
|
255
|
+
continue;
|
|
256
|
+
}
|
|
257
|
+
if (key === "requireImportSource") {
|
|
258
|
+
if (value !== false && typeof value !== "string") {
|
|
259
|
+
throw new TypeError(
|
|
260
|
+
"@solidjs/compiler `requireImportSource` option must be false or a string"
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
if (value !== false) nativeOptions.requireImportSource = value;
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
266
|
+
if (key === "wrapConditionals") {
|
|
267
|
+
if (typeof value !== "boolean") {
|
|
268
|
+
throw new TypeError("@solidjs/compiler `wrapConditionals` option must be boolean");
|
|
269
|
+
}
|
|
270
|
+
nativeOptions.wrapConditionals = value;
|
|
271
|
+
continue;
|
|
272
|
+
}
|
|
273
|
+
if (key === "validate") {
|
|
274
|
+
if (typeof value !== "boolean") {
|
|
275
|
+
throw new TypeError("@solidjs/compiler `validate` option must be boolean");
|
|
276
|
+
}
|
|
277
|
+
nativeOptions.validate = value;
|
|
278
|
+
continue;
|
|
279
|
+
}
|
|
280
|
+
if (nativeOptionKeys.has(key)) {
|
|
281
|
+
if (key === "renderers") validateRenderers(value);
|
|
282
|
+
nativeOptions[key] = value;
|
|
283
|
+
continue;
|
|
284
|
+
}
|
|
285
|
+
throw new Error(`@solidjs/compiler received unknown option \`${key}\``);
|
|
286
|
+
}
|
|
287
|
+
return nativeOptions;
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
function validateRenderers(renderers) {
|
|
291
|
+
if (renderers == null) return;
|
|
292
|
+
if (!Array.isArray(renderers)) {
|
|
293
|
+
throw new TypeError("@solidjs/compiler `renderers` option must be an array");
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
for (const renderer of renderers) {
|
|
297
|
+
if (typeof renderer !== "object" || renderer == null || Array.isArray(renderer)) {
|
|
298
|
+
throw new TypeError("@solidjs/compiler renderer entries must be objects");
|
|
299
|
+
}
|
|
300
|
+
for (const key of Object.keys(renderer)) {
|
|
301
|
+
if (key !== "name" && key !== "moduleName" && key !== "elements") {
|
|
302
|
+
throw new Error(`@solidjs/compiler received unknown renderer option \`${key}\``);
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
if (renderer.name !== "dom") {
|
|
306
|
+
throw new Error(
|
|
307
|
+
"@solidjs/compiler dynamic renderers only support the `dom` renderer override"
|
|
308
|
+
);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
function platformArchSuffix() {
|
|
314
|
+
const { platform, arch } = process;
|
|
315
|
+
if (platform === "darwin" && (arch === "x64" || arch === "arm64")) return `darwin-${arch}`;
|
|
316
|
+
if (platform === "linux" && (arch === "x64" || arch === "arm64")) return `linux-${arch}-gnu`;
|
|
317
|
+
if (platform === "win32" && arch === "x64") return "win32-x64-msvc";
|
|
318
|
+
return null;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
function tryPackage(packageName) {
|
|
322
|
+
try {
|
|
323
|
+
return { binding: require(packageName) };
|
|
324
|
+
} catch (error) {
|
|
325
|
+
if (isMissingPackage(error, packageName)) return { missing: true };
|
|
326
|
+
return { error };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
function requireBinding() {
|
|
331
|
+
const explicit = process.env.SOLID_COMPILER_NATIVE;
|
|
332
|
+
if (explicit) return require(explicit);
|
|
333
|
+
|
|
334
|
+
const forceWasi = process.env.NAPI_RS_FORCE_WASI;
|
|
335
|
+
if (forceWasi === "true" || forceWasi === "error") {
|
|
336
|
+
const wasi = requireWasi();
|
|
337
|
+
if (wasi) return wasi;
|
|
338
|
+
if (forceWasi === "error") {
|
|
339
|
+
throw new Error("WASI binding not found and NAPI_RS_FORCE_WASI is set to error");
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
let nativeError;
|
|
344
|
+
const suffix = platformArchSuffix();
|
|
345
|
+
|
|
346
|
+
if (suffix) {
|
|
347
|
+
const next = tryPackage(`@solidjs/compiler-${suffix}`);
|
|
348
|
+
if (next.binding) return next.binding;
|
|
349
|
+
if (next.error) nativeError = next.error;
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
const localCandidates = [];
|
|
353
|
+
if (suffix) localCandidates.push(`compiler.${suffix}.node`);
|
|
354
|
+
localCandidates.push("compiler.node");
|
|
355
|
+
for (const file of localCandidates) {
|
|
356
|
+
const full = path.join(__dirname, file);
|
|
357
|
+
if (fs.existsSync(full)) {
|
|
358
|
+
try {
|
|
359
|
+
return require(full);
|
|
360
|
+
} catch (error) {
|
|
361
|
+
nativeError = error;
|
|
362
|
+
break;
|
|
363
|
+
}
|
|
364
|
+
}
|
|
365
|
+
}
|
|
366
|
+
|
|
367
|
+
const wasi = requireWasi();
|
|
368
|
+
if (wasi) return wasi;
|
|
369
|
+
|
|
370
|
+
if (nativeError) {
|
|
371
|
+
nativeError.message +=
|
|
372
|
+
"\nThe native binding could not be loaded and the optional " +
|
|
373
|
+
"@solidjs/compiler-wasm32-wasi fallback is not installed.";
|
|
374
|
+
throw nativeError;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
throw new Error(
|
|
378
|
+
`Could not find an @solidjs/compiler binding for ${process.platform}-${process.arch}` +
|
|
379
|
+
(suffix
|
|
380
|
+
? ` (expected @solidjs/compiler-${suffix}, @solidjs/compiler-wasm32-wasi, or a local build)`
|
|
381
|
+
: " (no prebuilt binary is published for this platform)") +
|
|
382
|
+
". Install with WASM support or run `pnpm run build` in packages/compiler."
|
|
383
|
+
);
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
function requireWasi() {
|
|
387
|
+
const next = tryPackage("@solidjs/compiler-wasm32-wasi");
|
|
388
|
+
if (next.binding) return next.binding;
|
|
389
|
+
if (next.error) throw next.error;
|
|
390
|
+
|
|
391
|
+
const localWasi = path.join(__dirname, "compiler.wasi.cjs");
|
|
392
|
+
if (fs.existsSync(localWasi)) return require(localWasi);
|
|
393
|
+
return null;
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
function isMissingPackage(error, packageName) {
|
|
397
|
+
return (
|
|
398
|
+
error &&
|
|
399
|
+
error.code === "MODULE_NOT_FOUND" &&
|
|
400
|
+
typeof error.message === "string" &&
|
|
401
|
+
error.message.includes(`'${packageName}'`)
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
module.exports = {
|
|
406
|
+
transform,
|
|
407
|
+
transformAsync,
|
|
408
|
+
transformDirectives,
|
|
409
|
+
transformDirectivesAsync,
|
|
410
|
+
transformLazy,
|
|
411
|
+
transformLazyAsync,
|
|
412
|
+
transformRefresh,
|
|
413
|
+
transformRefreshAsync
|
|
414
|
+
};
|
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.2",
|
|
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",
|
|
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.2",
|
|
66
|
+
"@solidjs/compiler-darwin-arm64": "2.0.0-rc.2",
|
|
67
|
+
"@solidjs/compiler-linux-x64-gnu": "2.0.0-rc.2",
|
|
68
|
+
"@solidjs/compiler-linux-arm64-gnu": "2.0.0-rc.2",
|
|
69
|
+
"@solidjs/compiler-win32-x64-msvc": "2.0.0-rc.2",
|
|
70
|
+
"@solidjs/compiler-wasm32-wasi": "2.0.0-rc.2"
|
|
71
|
+
}
|
|
72
|
+
}
|
package/types.d.ts
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
export interface TransformOptions {
|
|
2
|
+
filename?: string;
|
|
3
|
+
/** Default `"@solidjs/web"`. */
|
|
4
|
+
moduleName?: string;
|
|
5
|
+
generate?: "dom" | "ssr" | "universal" | "dynamic";
|
|
6
|
+
hydratable?: boolean;
|
|
7
|
+
dev?: boolean;
|
|
8
|
+
sourceMap?: boolean;
|
|
9
|
+
contextToCustomElements?: boolean;
|
|
10
|
+
delegateEvents?: boolean;
|
|
11
|
+
delegatedEvents?: string[];
|
|
12
|
+
omitQuotes?: boolean;
|
|
13
|
+
omitAttributeSpacing?: boolean;
|
|
14
|
+
inlineStyles?: boolean;
|
|
15
|
+
effectWrapper?: "effect" | false;
|
|
16
|
+
wrapConditionals?: boolean;
|
|
17
|
+
memoWrapper?: "memo" | false;
|
|
18
|
+
staticMarker?: string;
|
|
19
|
+
validate?: boolean;
|
|
20
|
+
omitNestedClosingTags?: boolean;
|
|
21
|
+
omitLastClosingTag?: boolean;
|
|
22
|
+
serverComponents?: boolean;
|
|
23
|
+
/** Default `["For", "Show", "Switch", "Match", "Loading", "Reveal", "Portal", "Repeat", "Dynamic", "Errored"]`. */
|
|
24
|
+
builtIns?: string[];
|
|
25
|
+
requireImportSource?: false | string;
|
|
26
|
+
renderers?: RendererOption[];
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export interface RendererOption {
|
|
30
|
+
name: string;
|
|
31
|
+
moduleName?: string;
|
|
32
|
+
elements: string[];
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
export interface TransformResult {
|
|
36
|
+
code: string;
|
|
37
|
+
map?: string | null;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function transform(code: string, options?: TransformOptions | null): TransformResult;
|
|
41
|
+
export function transformAsync(
|
|
42
|
+
code: string,
|
|
43
|
+
options?: TransformOptions | null
|
|
44
|
+
): Promise<TransformResult>;
|
|
45
|
+
|
|
46
|
+
export interface DirectiveImportDefinition {
|
|
47
|
+
kind?: "named" | "default";
|
|
48
|
+
name?: string;
|
|
49
|
+
source: string;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Options for the experimental `"use server"` directive pass. Applies to
|
|
54
|
+
* plain `.js`/`.ts` modules as well as JSX/TSX.
|
|
55
|
+
*/
|
|
56
|
+
export interface TransformDirectivesOptions {
|
|
57
|
+
/** Required — function IDs hash the root-relative file path. */
|
|
58
|
+
filename: string;
|
|
59
|
+
/** Project root for ID hashing. Defaults to the working directory. */
|
|
60
|
+
root?: string;
|
|
61
|
+
/**
|
|
62
|
+
* `"server"` keeps the module and registers extracted functions;
|
|
63
|
+
* `"client"` replaces them with reference proxies and strips server-only
|
|
64
|
+
* code.
|
|
65
|
+
*/
|
|
66
|
+
mode: "server" | "client";
|
|
67
|
+
/** `"development"` appends function names to generated IDs. */
|
|
68
|
+
env?: "production" | "development";
|
|
69
|
+
/** @default "use server" */
|
|
70
|
+
directive?: string;
|
|
71
|
+
sourceMap?: boolean;
|
|
72
|
+
/** Runtime import for `registerServerReference` (server output). */
|
|
73
|
+
register?: DirectiveImportDefinition;
|
|
74
|
+
/** Runtime import for `createServerReference` (both outputs). */
|
|
75
|
+
create?: DirectiveImportDefinition;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/** One extracted server function, for building a bundler manifest. */
|
|
79
|
+
export interface ServerFunctionMeta {
|
|
80
|
+
/** The wire ID (`<hash>-<count>[-<name>]`). */
|
|
81
|
+
id: string;
|
|
82
|
+
name: string;
|
|
83
|
+
/** Export names bound to this function (module-level directives only). */
|
|
84
|
+
exports: string[];
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface TransformDirectivesResult {
|
|
88
|
+
code: string;
|
|
89
|
+
map?: string | null;
|
|
90
|
+
/** False when the module contained no matching directive. */
|
|
91
|
+
valid: boolean;
|
|
92
|
+
functions: ServerFunctionMeta[];
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export function transformDirectives(
|
|
96
|
+
code: string,
|
|
97
|
+
options: TransformDirectivesOptions
|
|
98
|
+
): TransformDirectivesResult;
|
|
99
|
+
export function transformDirectivesAsync(
|
|
100
|
+
code: string,
|
|
101
|
+
options: TransformDirectivesOptions
|
|
102
|
+
): Promise<TransformDirectivesResult>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Options for the experimental `lazy()` module-URL pass (ported from
|
|
106
|
+
* vite-plugin-solid's `lazy-module-url` Babel plugin).
|
|
107
|
+
*/
|
|
108
|
+
export interface TransformLazyOptions {
|
|
109
|
+
/**
|
|
110
|
+
* Mirrors the Babel plugin: without a filename the pass is a no-op (the
|
|
111
|
+
* emitted placeholder is only useful to a bundler resolving relative to a
|
|
112
|
+
* module id).
|
|
113
|
+
*/
|
|
114
|
+
filename?: string;
|
|
115
|
+
sourceMap?: boolean;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
export function transformLazy(code: string, options?: TransformLazyOptions | null): TransformResult;
|
|
119
|
+
export function transformLazyAsync(
|
|
120
|
+
code: string,
|
|
121
|
+
options?: TransformLazyOptions | null
|
|
122
|
+
): Promise<TransformResult>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Options for the experimental solid-refresh HMR pass (ported from the
|
|
126
|
+
* `solid-refresh` Babel plugin, `jsx: false` mode). Dev-only.
|
|
127
|
+
*/
|
|
128
|
+
export interface TransformRefreshOptions {
|
|
129
|
+
/**
|
|
130
|
+
* Used for `location` metadata (cwd-relative, matching the Babel plugin)
|
|
131
|
+
* and to pick the parser dialect. Without it no locations are emitted.
|
|
132
|
+
*/
|
|
133
|
+
filename?: string;
|
|
134
|
+
/**
|
|
135
|
+
* Selects the HMR API: `import.meta.hot` (esm/vite),
|
|
136
|
+
* `import.meta.webpackHot` (webpack5/rspack-esm) or `module.hot`
|
|
137
|
+
* (standard).
|
|
138
|
+
* @default "standard"
|
|
139
|
+
*/
|
|
140
|
+
bundler?: "esm" | "vite" | "webpack5" | "rspack-esm" | "standard";
|
|
141
|
+
/**
|
|
142
|
+
* Wrap top-level `render()`/`hydrate()` calls (imported from
|
|
143
|
+
* `@solidjs/web`) with `hot.dispose` cleanup.
|
|
144
|
+
* @default true
|
|
145
|
+
*/
|
|
146
|
+
fixRender?: boolean;
|
|
147
|
+
/**
|
|
148
|
+
* Emit per-component `signature`/`dependencies` metadata for granular HMR.
|
|
149
|
+
* @default true
|
|
150
|
+
*/
|
|
151
|
+
granular?: boolean;
|
|
152
|
+
/**
|
|
153
|
+
* The Babel plugin's JSX-granularity mode is not ported; only `false` is
|
|
154
|
+
* accepted (what vite-plugin-solid passes).
|
|
155
|
+
*/
|
|
156
|
+
jsx?: false;
|
|
157
|
+
/**
|
|
158
|
+
* Module the runtime helpers (`$$registry`, `$$component`, `$$refresh`,
|
|
159
|
+
* `$$decline`) are imported from. The dev-only `solid-js/refresh` entry
|
|
160
|
+
* exposes the same frozen ABI.
|
|
161
|
+
* @default "solid-refresh"
|
|
162
|
+
*/
|
|
163
|
+
importSource?: string;
|
|
164
|
+
sourceMap?: boolean;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
export function transformRefresh(
|
|
168
|
+
code: string,
|
|
169
|
+
options?: TransformRefreshOptions | null
|
|
170
|
+
): TransformResult;
|
|
171
|
+
export function transformRefreshAsync(
|
|
172
|
+
code: string,
|
|
173
|
+
options?: TransformRefreshOptions | null
|
|
174
|
+
): Promise<TransformResult>;
|