@dom-expressions/jsx-compiler 0.50.0-next.17 → 0.50.0-next.18

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 (3) hide show
  1. package/README.md +46 -9
  2. package/index.js +59 -9
  3. package/package.json +23 -9
package/README.md CHANGED
@@ -15,6 +15,29 @@ matching your platform automatically through `optionalDependencies`. On other
15
15
  platforms, build from source with `pnpm run build` inside
16
16
  `packages/jsx-compiler` (requires a Rust toolchain).
17
17
 
18
+ ### WebAssembly and StackBlitz
19
+
20
+ The compiler also ships a WASI fallback for environments such as StackBlitz
21
+ WebContainers, where Node.js reports a native platform but cannot load `.node`
22
+ addons. Enable the `wasm32` optional dependency when installing:
23
+
24
+ ```bash
25
+ npm install --cpu=wasm32 @dom-expressions/jsx-compiler
26
+ ```
27
+
28
+ With pnpm, add the installation architecture to `pnpm-workspace.yaml`:
29
+
30
+ ```yaml
31
+ supportedArchitectures:
32
+ cpu:
33
+ - current
34
+ - wasm32
35
+ ```
36
+
37
+ The normal package entry point prefers a native binding and falls back to
38
+ `@dom-expressions/jsx-compiler-wasm32-wasi` when native addons are unavailable.
39
+ Set `NAPI_RS_FORCE_WASI=error` to require the WASI binding for testing.
40
+
18
41
  ## Usage
19
42
 
20
43
  This package exposes a compiler backend API. It is not a Vite, Rollup, or Babel
@@ -136,8 +159,9 @@ Supported options track the Babel plugin where currently implemented:
136
159
  - `omitQuotes`
137
160
  - `omitAttributeSpacing`
138
161
  - `inlineStyles`
139
- - `effectWrapper`: `"effect"` or `false`
140
- - paired wrapperless mode: `wrapConditionals: false` with `memoWrapper: false`
162
+ - `effectWrapper`: custom import name string, or `false` to disable
163
+ - `memoWrapper`: custom import name string, or `false` to disable
164
+ - `wrapConditionals`
141
165
  - `staticMarker`
142
166
  - `validate`
143
167
  - `omitNestedClosingTags`
@@ -146,6 +170,22 @@ Supported options track the Babel plugin where currently implemented:
146
170
  - `requireImportSource`
147
171
  - `renderers`
148
172
 
173
+ ## Performance
174
+
175
+ Compared against `@dom-expressions/babel-plugin-jsx` compiling identical
176
+ sources under identical options (Apple Silicon, release build, in-process,
177
+ median of 7 iterations after warmup — run `pnpm bench` in this package to
178
+ reproduce on your machine):
179
+
180
+ | Workload | babel-plugin-jsx | jsx-compiler | Speedup |
181
+ | ----------------------------------------------- | ---------------: | -----------: | ------: |
182
+ | Fixture corpus (88 files, 174 KB, all 10 modes) | 151 ms | 8.4 ms | 18x |
183
+ | 129 KB single module | 230 ms | 4.6 ms | 50x |
184
+ | 1 MB single module | 10,164 ms | 39 ms | 258x |
185
+
186
+ Native throughput stays flat at ~20–27 MB/s as input grows, while Babel's
187
+ per-file cost grows super-linearly — so the gap widens with file size.
188
+
149
189
  ## Current Scope
150
190
 
151
191
  This package is the AST-native compiler backend. It currently has checked fixture coverage for
@@ -159,10 +199,10 @@ the DOM, hydratable DOM, dev hydratable DOM, SSR, hydratable SSR, universal, dyn
159
199
  attribute handling covered by the checked fixture suites for those targets
160
200
  - Solid-compatible defaults such as `contextToCustomElements: true`
161
201
  - option coverage for `hydratable`, `dev`, `delegateEvents`, `delegatedEvents`,
162
- `omitQuotes`, `omitAttributeSpacing`, `inlineStyles`, `effectWrapper: false`,
163
- paired `wrapConditionals: false` / `memoWrapper: false`, `requireImportSource`,
164
- `staticMarker`, `validate`, `omitNestedClosingTags`, `omitLastClosingTag`,
165
- `builtIns`, and dynamic `renderers`
202
+ `omitQuotes`, `omitAttributeSpacing`, `inlineStyles`, `effectWrapper`,
203
+ `memoWrapper`, `wrapConditionals`, `requireImportSource`, `staticMarker`,
204
+ `validate`, `omitNestedClosingTags`, `omitLastClosingTag`, `builtIns`, and
205
+ dynamic `renderers`
166
206
  - source maps for the implemented path
167
207
 
168
208
  ## Not Implemented Yet
@@ -172,8 +212,6 @@ The compiler intentionally rejects unsupported features instead of pretending to
172
212
  - DOM `namespaceElements` sections that the current Oxc parser rejects before transform
173
213
  (for example, hyphenated JSX member segments)
174
214
  - arbitrary custom renderer names beyond dynamic DOM renderer override plus universal fallback
175
- - custom `effectWrapper` / `memoWrapper` helper names
176
- - unpaired `wrapConditionals: false` or `memoWrapper: false`
177
215
  - unknown/custom namespaced DOM attributes outside known runtime namespaces such as `xlink`
178
216
 
179
217
  ## Architecture
@@ -198,4 +236,3 @@ The module layout mirrors the Babel plugin shape where possible:
198
236
  - `src/ssr/transform.rs`
199
237
  - `src/universal/mod.rs`
200
238
  - `src/universal/transform.rs`
201
-
package/index.js CHANGED
@@ -3,7 +3,7 @@
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
5
 
6
- const native = requireNative();
6
+ const native = requireBinding();
7
7
 
8
8
  function transform(code, options) {
9
9
  if (typeof code !== "string") {
@@ -152,10 +152,20 @@ function platformArchSuffix() {
152
152
  return null;
153
153
  }
154
154
 
155
- function requireNative() {
155
+ function requireBinding() {
156
156
  const explicit = process.env.JSX_DOM_EXPRESSIONS_COMPILER_NATIVE;
157
157
  if (explicit) return require(explicit);
158
158
 
159
+ const forceWasi = process.env.NAPI_RS_FORCE_WASI;
160
+ if (forceWasi === "true" || forceWasi === "error") {
161
+ const wasi = requireWasi();
162
+ if (wasi) return wasi;
163
+ if (forceWasi === "error") {
164
+ throw new Error("WASI binding not found and NAPI_RS_FORCE_WASI is set to error");
165
+ }
166
+ }
167
+
168
+ let nativeError;
159
169
  const suffix = platformArchSuffix();
160
170
 
161
171
  // Local builds (napi build output) take precedence for development.
@@ -164,23 +174,63 @@ function requireNative() {
164
174
  localCandidates.push("jsx-compiler.node");
165
175
  for (const file of localCandidates) {
166
176
  const full = path.join(__dirname, file);
167
- if (fs.existsSync(full)) return require(full);
177
+ if (fs.existsSync(full)) {
178
+ try {
179
+ return require(full);
180
+ } catch (error) {
181
+ nativeError = error;
182
+ break;
183
+ }
184
+ }
168
185
  }
169
186
 
170
- if (suffix) {
187
+ if (!nativeError && suffix) {
188
+ const packageName = `@dom-expressions/jsx-compiler-${suffix}`;
171
189
  try {
172
- return require(`@dom-expressions/jsx-compiler-${suffix}`);
190
+ return require(packageName);
173
191
  } catch (error) {
174
- if (error.code !== "MODULE_NOT_FOUND") throw error;
192
+ if (!isMissingPackage(error, packageName)) nativeError = error;
175
193
  }
176
194
  }
177
195
 
196
+ const wasi = requireWasi();
197
+ if (wasi) return wasi;
198
+
199
+ if (nativeError) {
200
+ nativeError.message +=
201
+ "\nThe native binding could not be loaded and the optional " +
202
+ "@dom-expressions/jsx-compiler-wasm32-wasi fallback is not installed.";
203
+ throw nativeError;
204
+ }
205
+
178
206
  throw new Error(
179
- `Could not find the native @dom-expressions/jsx-compiler binary for ${process.platform}-${process.arch}` +
207
+ `Could not find an @dom-expressions/jsx-compiler binding for ${process.platform}-${process.arch}` +
180
208
  (suffix
181
- ? ` (expected the @dom-expressions/jsx-compiler-${suffix} package or a local build)`
209
+ ? ` (expected @dom-expressions/jsx-compiler-${suffix}, @dom-expressions/jsx-compiler-wasm32-wasi, or a local build)`
182
210
  : " (no prebuilt binary is published for this platform)") +
183
- ". Run `pnpm run build` in packages/jsx-compiler to build from source."
211
+ ". Install with WASM support or run `pnpm run build` in packages/jsx-compiler."
212
+ );
213
+ }
214
+
215
+ function requireWasi() {
216
+ const localWasi = path.join(__dirname, "jsx-compiler.wasi.cjs");
217
+ if (fs.existsSync(localWasi)) return require(localWasi);
218
+
219
+ const packageName = "@dom-expressions/jsx-compiler-wasm32-wasi";
220
+ try {
221
+ return require(packageName);
222
+ } catch (error) {
223
+ if (!isMissingPackage(error, packageName)) throw error;
224
+ return null;
225
+ }
226
+ }
227
+
228
+ function isMissingPackage(error, packageName) {
229
+ return (
230
+ error &&
231
+ error.code === "MODULE_NOT_FOUND" &&
232
+ typeof error.message === "string" &&
233
+ error.message.includes(`'${packageName}'`)
184
234
  );
185
235
  }
186
236
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@dom-expressions/jsx-compiler",
3
3
  "description": "A JSX to DOM Expressions compiler implemented with Oxc",
4
- "version": "0.50.0-next.17",
4
+ "version": "0.50.0-next.18",
5
5
  "author": "Ryan Carniato",
6
6
  "license": "MIT",
7
7
  "repository": {
@@ -31,22 +31,36 @@
31
31
  "aarch64-apple-darwin",
32
32
  "x86_64-unknown-linux-gnu",
33
33
  "aarch64-unknown-linux-gnu",
34
- "x86_64-pc-windows-msvc"
35
- ]
34
+ "x86_64-pc-windows-msvc",
35
+ "wasm32-wasip1-threads"
36
+ ],
37
+ "wasm": {
38
+ "browser": {
39
+ "fs": false,
40
+ "asyncInit": false,
41
+ "buffer": false,
42
+ "errorEvent": true
43
+ }
44
+ }
36
45
  },
37
46
  "devDependencies": {
38
- "@napi-rs/cli": "^3.6.2"
47
+ "@emnapi/core": "^1.11.2",
48
+ "@emnapi/runtime": "^1.11.2",
49
+ "@napi-rs/cli": "^3.7.2",
50
+ "@napi-rs/wasm-runtime": "^1.1.6"
39
51
  },
40
52
  "optionalDependencies": {
41
- "@dom-expressions/jsx-compiler-darwin-x64": "0.50.0-next.17",
42
- "@dom-expressions/jsx-compiler-darwin-arm64": "0.50.0-next.17",
43
- "@dom-expressions/jsx-compiler-linux-x64-gnu": "0.50.0-next.17",
44
- "@dom-expressions/jsx-compiler-linux-arm64-gnu": "0.50.0-next.17",
45
- "@dom-expressions/jsx-compiler-win32-x64-msvc": "0.50.0-next.17"
53
+ "@dom-expressions/jsx-compiler-darwin-x64": "0.50.0-next.18",
54
+ "@dom-expressions/jsx-compiler-darwin-arm64": "0.50.0-next.18",
55
+ "@dom-expressions/jsx-compiler-linux-x64-gnu": "0.50.0-next.18",
56
+ "@dom-expressions/jsx-compiler-linux-arm64-gnu": "0.50.0-next.18",
57
+ "@dom-expressions/jsx-compiler-win32-x64-msvc": "0.50.0-next.18",
58
+ "@dom-expressions/jsx-compiler-wasm32-wasi": "0.50.0-next.18"
46
59
  },
47
60
  "scripts": {
48
61
  "build": "napi build --release --strip --manifest-path ./Cargo.toml",
49
62
  "build:debug": "napi build --manifest-path ./Cargo.toml",
63
+ "bench": "pnpm run build && node scripts/bench.mjs",
50
64
  "lint": "cargo clippy --manifest-path ./Cargo.toml -- -D warnings",
51
65
  "test": "pnpm run build:debug && jest --no-cache",
52
66
  "artifacts": "napi artifacts",