Skip to content

refactor: rewrite the compiler with oxc and magic-string - #2

Merged
davedbase merged 3 commits into
mainfrom
feat/oxc-compiler
Nov 11, 2026
Merged

davedbase merged 3 commits into
mainfrom
feat/oxc-compiler

Conversation

@lxsmnsyc

@lxsmnsyc lxsmnsyc commented Nov 11, 2026 •

Copy link
Copy Markdown
Member

Depends on #1

Rewrites the DynamicImage compiler with oxc-parser and magic-string. This drops Babel from the package's dependencies and gives the unplugin a source map.

Changes

  • Parse with oxc-parser and apply each rewrite as a span edit over a MagicString. The output comes with a hires source map.
  • A small walker in src/compiler/ast.ts skips TypeScript type keys, so types are no longer searched for identifiers.
  • The unplugin filters ids with unplugin-utils instead of @solid-mediakit/shared, with the same default include and exclude.
  • A fixture runner in vitest replaces babel-plugin-tester. It compares prettier-formatted output. UPDATE=1 writes the outputs; without it, a fixture with no output fails.
  • New fixtures: object-values, optional-chain, typescript, aliased-import.

Behavior changes from the Babel version:

  • Object property values inside a DynamicImage become server function arguments. The Babel version skipped them, so style={{ color: color() }} left color() unresolved inside the server function.
  • A shorthand property keeps its key: { a } becomes { a: r0 }.
  • An optional chain such as user?.profile.name is one argument.
  • Generated server functions follow source order.
  • A module without a DynamicImage is returned unchanged (the transform returns null).
  • import { createMemo as memo } no longer counts as importing createMemo, so the compiler adds its own import. Before, the generated code threw a ReferenceError.
  • Generated imports from one source are one statement.

Proof

  • object-values on the parent branch: the server function renders style={{ color: color(), background, [r0]: size }}, where color, background and size do not exist on the server.
  • On this branch: pnpm --filter @solidjs/ogp test passes, and the fixture renders style={{ color: r0, background: r1, [r2]: r3 }} with all four values passed in.

Changeset

  • patch (compiler-on-oxc.md): the existing compiler behaves differently.

Checks

  • Local: lint pass, format pass, typecheck pass, build pass, tests pass (45).
  • npm pack --dry-run lists the same files.
  • The built dist/unplugin.js compiles a sample .tsx route and returns a source map.

Roadmap

  • none

Cost

  • Removed dependencies: @babel/core, @solid-mediakit/shared. Removed dev dependencies: babel-plugin-tester, @babel/preset-typescript, @types/babel__core.
  • New dependencies: oxc-parser, magic-string, unplugin-utils. New dev dependency: prettier (fixture comparison).

Risk

  • Existing fixture outputs differ only in formatting, and in the order of generated functions in multiple-images.
  • A <DynamicImage> nested inside another one stays as a literal element in the generated server function, with no error. This PR does not change that, and it is left for a separate fix.
  • The compiler folder turns off the no-unsafe-* lint rules, because it works on an untyped structural AST, as in solid-styled.

🤖 Generated with Claude Code

@socket-security

socket-security Bot commented Nov 11, 2026 •

Copy link
Copy Markdown

Review the following changes in direct dependencies. Learn more about Socket for GitHub.

Diff Package Supply Chain
Security
Vulnerability Quality Maintenance License
Addedunplugin-utils@​0.3.21001009181100
Addedoxc-parser@​0.158.08910010096100
Addedmagic-string@​1.4.510010010096100
Updatedprettier@​3.9.6 ⏵ 3.9.109810097 +197 +2100

View full report

lxsmnsyc and others added 3 commits November 11, 2026 22:13
The compiler parses with oxc-parser and applies every rewrite as a span
edit over a MagicString, which also gives a hires source map. This drops
@babel/core and @solid-mediakit/shared from the dependencies, and
babel-plugin-tester and @babel/preset-typescript from the tests.

Behavior changes from the Babel version:
- Object property values inside a DynamicImage become arguments. Babel
  skipped them, so style={{ color: color() }} left color() unresolved in
  the server function.
- A shorthand property keeps its key ({ a } becomes { a: r0 }).
- An optional chain is one argument.
- TypeScript type annotations are skipped instead of being searched for
  identifiers.
- Generated server functions follow source order.
- A module without a DynamicImage is returned unchanged (null).

The fixture runner compares prettier-formatted output, and new fixtures
cover object values, optional chains and TypeScript.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
import { createMemo as memo } does not bind createMemo, but the compiler
treated it as present and the generated code threw a ReferenceError.
Generated imports from one source are now one statement, and a fixture
without an output fails unless UPDATE=1 is set.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Base automatically changed from chore/repo-setup to main November 11, 2026 14:21
@davedbase
davedbase marked this pull request as ready for review November 11, 2026 14:21
@davedbase
davedbase merged commit 5766977 into main Nov 11, 2026
4 checks passed
@davedbase
davedbase deleted the feat/oxc-compiler branch November 11, 2026 14:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants