ReviewOS
@if (!issue)

No such issue

stacks/stx has no issue numbered 1983.

@else

stacks/stx

feat(stx-native): the compiler re-parses .stx and cannot compile the component library

#1983
Closed glennmichael123 opened this 9 days ago ·
9 days ago

Summary

packages/stx-native compiles .stx to a native view IR with its own lexer and parser (src/compiler/parser.ts, 742 lines). That parser is not stx's parser, and it cannot read real stx templates — so none of @stacksjs/components compiles. Going native today would mean rewriting all 102 components against <View>/<Text> primitives.

It does not need to. stx's own pipeline already produces exactly the right input.

Evidence

Feeding real library components to compileSTX:

ComponentResult
packages/components/src/ui/button/Button.stxthrows Expected > or /> at line 54, got TEXT
packages/components/src/ui/card/Card.stxthrows No root element found in template
packages/components/src/ui/mobile/TabBar.stxcompiles to a single Text node, with a still in the IR

The third is the dangerous one: it does not fail, it silently produces a wrong tree.

What the parser does not handle: <script server>, @if / @foreach, inside attributes, component composition, slots — i.e. most of what an stx component is.

The parser is not useless. Given a hand-written template of native primitives it is correct, and the headwind compiler behind it is genuinely good:

<View class="flex-1 flex-col justify-center items-center p-4 bg-blue-500">
  <Text class="text-white text-lg font-bold">Wildloop</Text>
</View>
{ "type": "View", "style": { "flex": 1, "flexDirection": "column", "justifyContent": "center",
  "alignItems": "center", "padding": 16, "backgroundColor": "#3b82f6" },
  "children": [ { "type": "Text", "style": { "color": "#ffffff", "fontSize": 18, "fontWeight": "bold" } } ] }

The change

stx's renderer already emits output with client bindings intact — :show, x-text, @click survive into the HTML precisely so the signals runtime can bind them. That output is the right input for the native compiler:

  • mapToNativeComponent (src/compiler/parser.ts:672) already maps div→View, span/p/h1→Text, button→Button, img→Image, input→TextInput
  • compileHeadwindToStyle already turns the class strings those components carry into native style
  • server scripts, directives, component expansion and slots are then handled by the pipeline that has 13,697 tests behind it, instead of by a second parser with none

Ask

  1. Replace the bespoke lexer/parser with a translator whose input is stx's rendered output (bindings preserved), not raw .stx.
  2. Keep ir.ts, headwind-to-style.ts and the component mapping — they are the parts worth keeping.
  3. Delete Lexer/Parser/parseSTX once the translator covers them, so there is one parser in the project rather than two that disagree.
9 days ago

Part of #1992 (stx-native: native views for wildloop).

9 days ago

Asks 1 and 2 are done; ask 3 is not, and I'd rather say why than close this.

The second parser is replaced

compileScreenSource (src/compiler/render-screen.ts) renders a screen with stx and translates the output. The tree is built with HTMLRewriter — Bun's own tokenizer — rather than a hand-written one, since replacing one bespoke parser with another would miss the point.

Measured over all 101 components in @stacksjs/components:

old parsertranslator
correct590
threw391
silently wrong570

Two of the three components this issue sampled have degraded since it was filed: Card.stx no longer throws, it returns a single Text node holding