Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
338 changes: 338 additions & 0 deletions docs/syntax-jsx-support.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,338 @@
# JSX syntax for Luau via `.luax` (experimental)

## Summary

This RFC proposes adding an **optional JSX-like syntax** to Luau, enabled only for files with a `.luax` extension and gated behind a feature flag, that desugars directly into existing Luau AST constructs (no separate transpile step).

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

.luaux might be a more appropriate extension for luau

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the CLI currently uses .lua for luau files, so I followed that convention

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Luau's official syntax extension is .luau and that is what is used everywhere, .lua is supported for backwards compatibility. That Roblox's internal tooling is too broken to support .luau consistently is a problem we aim to rectify, not something we would commit to making worse. Luau is not Lua, even if Lua 5.1 code can mostly run unchanged in Luau.

Copy link
Author

@DanFessler DanFessler Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

makes sense I'm all for switching it


## Motivation

JSX is a concise, widely understood syntax for describing UI trees. In Roblox’s ecosystem, UI is commonly built using React-like libraries (e.g. Roact/React), but Luau today requires verbose `createElement(...)` calls or helper builders.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

createElement is more like a flaw in the API design of React than that the language itself cannot express both properties and children. You can very clearly construct equivalents to XML structures in Luau with tables directly and no function calls at all:

<div class="foo">
    <child />
    <child data="other" />
</div>
{ tag = "div", class = "foo",
    { tag = "child" },
    { tag = "child", data = "other" },
 }

If you're willing to write a small bit of helper functions, you can even have:

div { class = "foo",
    child {},
    child { data = "other" }
}

You can try to make an argument that you nevertheless think XML syntax is so compelling as to be worth building into the language, but the motivation of "a library I use has an unpleasant API" is not really a problem that the language should be solving.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we updated the React API in this way, a whole bunch of problems line up neatly: Things like syntax highlighters and autocomplete will work perfectly with no code changes required on their part.

Copy link
Author

@DanFessler DanFessler Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument indeed boils down to compelling syntax for declarative tree structures, but the framing of "library I use has an unpleasant API" is a bit of a straw man (or at least I could have done better to justify it) as the RFC describes why this isn't just a React-motivated value-add - harnessing it with other UI frameworks, declarative scene compositions, behavior trees, state machines, etc - none of which require React to find justify utility.

Your proposed syntax using pure tables has less legibility than XML does as everything has the same "weight" or treatment for parts that mean very different things.

your third proposal is a bit better (though I still would argue not as good as XML for reason above, but now you have to require every intrinsic element before use which is overkill.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've long said createElement is an API so bad that to make it usable javascript had to create new syntax. We don't need to make the same mistake of creating new syntax to make a bad API usable. Just make a better API. aatxe's third example is a much better API, and is (almost) the API that the popular UI library Vide uses, and people using Vide don't think the language needs XML syntax.

tldr: the language should not change for React, React should change for the language


This proposal aims to:

- Make UI code **more readable** and **more ergonomic**.
- Preserve Luau’s “no mandatory build step” workflow by implementing parsing in the language front-end.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This point is very unconvincing because it only makes sense if you already believe that XML must be part of the syntax. If you don't, then making an optional tool that compiles your XML syntax to Luau (which is how this works in the actual web ecosystem you're cribbing it from) seems like the obvious solution.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the primary justification for "no build" is that Luau has already made a similar decision in regards to types which also could have been stripped via transpilation similar to TS->JS. Both approaches have their benefits, but one major win for going native is the amount of machinery/pipelines that become involved with build steps which is a common pain-point in the web ecosystem

- Avoid hard-coding any specific UI library into the language by desugaring to a **user-provided factory function**.

### Why JSX belongs at the language level (and is not “React syntax”)

A common concern is that JSX is “too specific” to React or UI frameworks to warrant native syntax. This RFC argues the opposite: **JSX is a general-purpose notation for declarative, structured function calls**, and the runtime meaning is entirely controlled by the `jsx` factory function chosen by the host/project.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

runtime meaning is entirely controlled by the jsx factory function chosen by the host/project

This is extremely jarring, and an immediate hard no for this feature. We will not add syntactic sugar that expands to a magically named function (calling is jsx is especially horrible since there is no JavaScript here in any way at all) that you just happen to ambiently assign to before using the sugar. Syntactic sugar in Luau must expand to something that is actually a "complete" piece of a program in Luau. You could propose, for instance, that the XML syntax expands to a mixed table, the actual data that exists in Luau, and then make your library process that correctly, but as-is this is a hard dealbreaker IMO.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can understand this perspective. Shipping a language feature that is by default broken does seem like a hard pill to swallow. Your proposal of defaulting to a data table is an elegant solution. Will revise.


In other words, JSX is not “React in the language”; it is:

- A compact syntax for constructing nested call trees (`jsx(tag, props, ...children)`).
- A convenient way to bind named parameters via record-like props (`{ key = value }`).
- A structured form that is easier for humans and tools to read than deeply nested function calls.

This makes JSX useful beyond UI, including data-driven scene graphs, entity graphs, or declarative configuration trees.

## Design

### Goals

- **No transpilation step**: JSX is parsed directly by Luau’s parser when enabled.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You don't need to say this. Anything that is a language feature is parsed by the language.

- **Opt-in**: no behavior change for `.lua` / `.luau` unless explicitly enabled.
- **Library-agnostic**: JSX lowers to an ordinary Luau call (`jsx(...)`) so projects can bind it to Roact/React/etc.
- **Minimize grammar ambiguity** with existing and future Luau syntax.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is also a requirement for all syntax proposals too, it doesn't need to be said to be a goal.


### Non-goals (initially)

These are intentionally excluded from the initial proposal to reduce ambiguity and implementation risk:

- Raw text children (e.g. `<Foo>hello</Foo>`)
- Fragments (e.g. `<>...</>`)
- Spread attributes (e.g. `<Foo {...props} />`)
- Namespaced attributes (e.g. `on:click=...`)
- JSX comments syntax

These can be added via follow-on RFCs once the core parsing approach proves robust.

### Opt-in surface area

**File extension**: `.luax` indicates JSX may appear in the file.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The file extension should clearly be .luaux, not .luax, but this doesn't make sense as an opt-in mechanism at all. Most Luau code today is written in environments where the script doesn't have an extension at all because it's not being written in a file system.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I can revise. Extension is just for cli use. My implementation also accepts a parseOptions.allowJsx for embedded contexts


**Feature flag**: The syntax must be gated by a flag (e.g. `FFlag::LuauJsx`) so it can ship experimentally and be rolled out carefully.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

All changes to the language must be flagged. This doesn't need to be in the RFC at all.


### Syntax

When enabled, an additional `simpleexp` form is introduced:

- `jsxElement ::= '<' jsxTagName jsxAttributes ( '/>' | '>' jsxChildren '</' jsxTagName '>' )`
- `jsxTagName ::= Name { '.' Name }`
- `jsxAttributes ::= { jsxAttribute }`
- `jsxAttribute ::= Name [ '=' ( String | '{' expr '}' ) ]`
- `jsxChildren ::= { jsxChild }`
- `jsxChild ::= jsxElement | '{' expr '}'`

Notes:

- `{ expr }` is used for embedding arbitrary Luau expressions in children and attribute values.
- Attribute shorthand (`<Foo disabled />`) is allowed and is equivalent to `disabled={true}`.

### Desugaring

JSX constructs are desugared in the parser into an ordinary Luau function call:
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is missing an example, it seems.


#### Tag lowering

- If the tag begins with a lowercase identifier and has no dots (e.g. `<frame />`), it lowers to a string tag: `"frame"`.
- Otherwise it lowers to an identifier/dotted expression (e.g. `<Foo.Bar />` lowers to `Foo.Bar`).
Comment on lines +78 to +79
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This feels very non-obvious, and like it would be a constant footgun for using this feature. Why is this the semantics you want to propose? Why are we not proposing a single, consistent semantics for tags?

Copy link
Author

@DanFessler DanFessler Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the convention of JSX, which alone is not justification for how we should do it, but does give the benefit of skill transferability for developers already familiar with it.

One counter argument against the convention is it makes mapping strings to intrinsic elements harder to manage as theres no guarantee your instances can match the casing (like Roblox's instances which typically use pascal case) which will usually require an implementer to provide a mapping from strings to intrinsic element names.

Again the largest argument against this is to leverage familiar widely adopted conventions.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

An alternative approach is to wrap intrinsics in quotes and anything else is a function


#### Props lowering

Attributes lower into a table literal (record fields). If there are no attributes, props lower to `nil`.

#### Children lowering

Children lower into additional positional arguments after `(tag, props)`.

#### Examples

Self-closing:

```lua
return <Foo bar={x} />
```

Desugars to (conceptually):

```lua
return jsx(Foo, { bar = x })
```

Lowercase tag:

```lua
return <frame />
```

Desugars to:

```lua
return jsx("frame", nil)
```

Nested child:

```lua
return <Foo><Bar /></Foo>
```

Desugars to:

```lua
return jsx(Foo, nil, jsx(Bar, nil))
```

Boolean attribute shorthand:

```lua
return <Foo disabled />
```

Desugars to:

```lua
return jsx(Foo, { disabled = true })
```

### Runtime / library integration

Luau does **not** include React/Roact. JSX desugars to a call to a function named `jsx`. Projects can provide this in whichever way fits their runtime:

- `jsx = React.createElement`
- `jsx = Roact.createElement`
- `jsx = MyCustomElementFactory`
Comment on lines +143 to +145
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This would be a strange oddity in the language. There is no feature in Luau whose behavior is defined by you assigning to a magic name. It's inconsistent, and the RFC says nothing of what you intend to happen when the user, inevitably, does not author one of these assignments to give it semantics. The default outcome would be "'nil' cannot be called" which is entirely divorced from what the user wrote, and would require them to understand exactly how this extension was implemented to know what is going on.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The intended result would indeed be "attempt to call nil" but as discussed above I agree this isn't ideal and should instead provide a functional default behavior of building a data table


This keeps the language neutral and allows different runtimes to adopt the syntax without coupling Luau to any one library.

#### Intrinsics mapping for lowercase tags
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This whole section feels like a direct argument for why the split behavior in your "tag lowering" section is a mistake. One piece of the design is creating a new problem that the other part solves. We should simply not create the problem.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Responded in another comment thread with justifications and alternative approach


Because lowercase tags lower to string values (e.g. `<frame />` → `jsx("frame", ...)`), most runtimes will want a small **lookup table** that maps these “intrinsic” names to the runtime’s native element types (for Roblox, typically Instance class names such as `"Frame"`).

For example:

```lua
local Intrinsics = {
frame = "Frame",
textlabel = "TextLabel",
uilistlayout = "UIListLayout",
-- ... (many more)
}

function jsx(tag, props, ...)
if type(tag) == "string" then
local className = Intrinsics[tag]
assert(className, ("Unknown intrinsic tag %q"):format(tag))
-- dispatch to your runtime using className
else
-- tag is a component (function/table/etc.) -> dispatch accordingly
end
end
```

This approach is explicit and predictable, avoids ambiguous casing rules, and allows aliases (`div = "Frame"`) if desired.

### Example: binding JSX to Fusion (community framework)

Fusion is a popular community framework for Roblox that uses a declarative style for Instances and reactive state. Without changing the JSX proposal, a project can adopt JSX by binding `jsx` to a Fusion element factory.

In practice, Fusion code commonly looks like `Fusion.New("Frame"){ ... }` and uses special keys like `Fusion.Children` for child lists. The exact adapter shape may vary by project, but a representative sketch is:

```lua
local Fusion = require(Packages.Fusion)
local New = Fusion.New
local Children = Fusion.Children

local Intrinsics = {
textlabel = "TextLabel",
frame = "Frame",
-- ...
}

-- One possible adapter shape:
-- jsx("frame", props, ...children) -> Fusion.New("Frame")(props)
-- jsx(FooComponent, props, ...children) -> FooComponent(props, ...children) (project-defined)
function jsx(tag, props, ...)
props = props or {}
props[Children] = { ... }

if type(tag) == "string" then
return New(Intrinsics[tag])(props)
else
-- component case is project-defined; this is just a sketch
return tag(props)
end
end

local State = Fusion.Value("Hello")

local ui =
<textlabel
Text={State}
Size={UDim2.fromScale(1, 1)}
/>
```

This example illustrates the key point: **JSX is a “call tree” surface syntax**, and Fusion (not Luau) defines the semantics.

### Example: non-UI usage (scene graph / “fiber-like” tree)

JSX can also be used for non-UI declarative graphs, e.g. building a 3D scene tree out of Roblox Instances (a “fiber-like” idea: a declarative tree that produces runtime objects).
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't really think we need to have an argument about the idea that XML syntax, whose entire raison d'etre is "authoring trees," can represent trees. Nobody is disputing that, the points of contention, as you can hopefully tell from the rest of the comments here, would be things like "what does embedded XML syntax in Luau mean?" and "does it even make sense for the language to concern itself with hybridizing itself with another language?" Those are the questions that this RFC needs to answer, and it does the former (though with details I think are insufficient to make the cut for the language), and seems to make no argument for the latter.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fair. Will make a revision with clearer arguments


```lua
local Intrinsics = {
model = "Model",
part = "Part",
pointlight = "PointLight",
attachment = "Attachment",
-- ...
}

-- A minimal Instance-producing factory:
function jsx(tag, props, ...)
assert(type(tag) == "string", "This example expects intrinsic (lowercase) tags")
local className = assert(Intrinsics[tag], ("Unknown intrinsic tag %q"):format(tag))

local inst = Instance.new(className)
props = props or {}

-- Apply properties
for k, v in pairs(props) do
inst[k] = v
end

-- Parent children to this instance
for _, child in ipairs({ ... }) do
if typeof(child) == "Instance" then
child.Parent = inst
end
end

return inst
end

local scene =
<model Name="Root">
<part Name="Ground" Anchored={true} Size={Vector3.new(50, 1, 50)} />
<part Name="Lamp" Position={Vector3.new(0, 5, 0)}>
<pointlight Brightness={3.5} Range={24} />
</part>
</model>
```

This keeps the same core idea: the tag is a string, props are a table, and children are a list. The meaning is defined by the chosen runtime factory (`jsx`), and JSX is simply a concise way to express the tree.

### Compatibility & ambiguity analysis

#### Backwards compatibility

With `.luax` + flag gating, existing `.lua`/`.luau` source is unaffected.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flags are flipped on and that leads to the feature being enabled, so flags are not an argument for existing programs being unaffected. Indeed, most users of Luau are running it in environments where every flag that starts with Luau is enabled by default.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll add details about parseOptions as mentioned above


Within `.luax`, code that uses `<` for comparisons (e.g. `a < b`) remains valid; JSX is only recognized when the parser expects a `simpleexp` and sees `<` as the next token in that position.

#### Grammar ambiguity concerns

Key ambiguity sources:

- `<` as binary operator vs JSX element start
- `</` sequences inside other contexts
- Interaction with existing type syntax, including explicit type instantiation (`<<T>>()`)

Mitigations:

- Restrict JSX recognition to positions where a `simpleexp` is parsed.
- Keep JSX syntax small initially (no raw text children, fragments, or spreads).
- Continue gating behind a flag to allow collecting real-world compatibility data.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Flagging is not a mitigation against syntax ambiguity. We are not going to add ambiguous syntax to Luau intentionally. So, either the syntax is unambiguous and can be considered for addition to the language, or it is ambiguous and needs to be redesigned with that consideration in mind.


### Editor integration & tooling considerations

Adding JSX affects:

- Tokenization/highlighting for `.luax`
- Parsing for autocomplete, formatter/pretty-printer, and incremental parse
- Error recovery expectations (JSX introduces new “paired delimiter” structures)

Mitigations:

- Keep JSX AST lowering to existing nodes so downstream tools operate on familiar AST shapes.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You earlier argued that this would not use a compilation step, and then this sentence is literally describing a compilation step. This clouds my understanding of what you intend a lot here.

Copy link
Author

@DanFessler DanFessler Dec 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The point being made here is that there are no new AST nodes to deal with here either internally by the interpreter or externally if there are other tools that do depend on luau's AST. I'm not arguing for a build step as part of this proposal.

That said, as mentioned in another comment, it may be desirable to introduce new AST nodes for exactly this reason - so external consumers can leverage them for things like syntax highlighting, formatting, etc. this should be revised to state there

should revised to say no new CST nodes, but would likely introduce new AST ones

(I'm making some assumptions about the internal workings of luau I need to double check)

- Ensure error recovery always makes progress to avoid hangs in partial code states.
- Treat `.luax` as a distinct language mode/extension for editors.

### Security / sandboxing considerations

The syntax itself is not inherently unsafe, but it makes calling a factory function extremely easy. Sandboxing concerns are the same as for ordinary function calls:

- The host controls which globals exist (including `jsx`).
- Environments can restrict or replace `jsx` to safe implementations.
Comment on lines +302 to +307
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh, this section gets at something that was not obvious at all earlier. You intend on the function being a built-in global provided by the embedder. That's certainly more reasonable than "the user will author an assignment statement somewhere," but the RFC needs to both be much, much clearer that that is the intention, and also needs to actually describe what the semantics of the builtin function is for standalone Luau. It's nevertheless still a design I find uncomfortable though because it feels like it immediately leads to questions for every embedder of "what framework do I privilege by making it the default here?" which is not something we want to require for users of the language.

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes sorry I'll make this point much clearer. I think the language default of outputting a structured table is a reasonable one that doesn't bless any particular framework.

In certain contexts, blessing is exactly what you want. My ideal for Roblox for example is it defaults to instantiating native instances (non-reactive). I have a demo of this behavior that feels great for Roblox


### Performance considerations

- Parsing adds new branches and lookahead, but is expected to be negligible compared to overall parsing cost.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a non-statement about the performance considerations. Earlier, you were saying that the syntax might be ambiguous (or rather, you were ambiguous about whether or not the syntax is unambiguously parseable). If it isn't, then there's considerable performance considerations that you're glossing over entirely here by just saying "it adds some new branches and lookahead." Adding more lookahead to the parser can entirely change the overall complexity class of it! That is not a trivial addition, and it is not something we can gloss over when designing new syntax.

- Desugaring into existing AST nodes avoids adding new runtime node kinds and keeps later stages (typechecking, codegen) unchanged.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Luau's code base works directly on a single AST today. You're proposing adding a multi-stage compiler effectively, which while reasonable is, again, a much more significant architectural change to the product than how you're trying to describe it here.


### Status

**Implemented (flagged / experimental)** in this repository as a spike:

- `FFlag::LuauJsx` gating
- `.luax` extension used to enable `ParseOptions::allowJsx`
- Parser desugars JSX to `jsx(tag, props, ...children)`
- Parser tests cover collision edge cases and error recovery

This RFC is still required before any user-facing rollout.

## Drawbacks

- Adds a significant new surface area to the language syntax, which increases learning and maintenance cost.
- JSX has a history of subtle grammar ambiguities; even with careful gating, future syntax additions may conflict.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is a huge drawback that runs dramatically against the interests of the language. You'll need to make a very, very strong case that embedding XML into Luau would be so useful as to be worth a miserable experience trying to make any other change to the syntax in the future.

- Editor/tooling support must be updated (highlighting, formatting, autocomplete), and mismatches can degrade UX.
- The “factory function” convention (`jsx`) is another community contract to standardize (or allow configuring).

## Alternatives

1. **Do nothing**: keep using `createElement(...)` calls and helper builders.
2. **Library-level DSL**: implement an embedded DSL using Lua syntax only (functions/tables/metatables). This avoids parser changes but is typically more verbose and harder to read.
3. **Transpile step**: introduce a `.luax -> .luau` transformer. This conflicts with the “interpreted/no build step” constraint and complicates tooling.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You invented the constraint that you cannot use a compiler, so if you want to argue that this option is a bad alternative, you need to do a lot more than write a single sentence saying it violates the constraint that you set up, but did not justify, earlier in the document. You should be answering why that constraint is in place, and justifying in detail why you believe having a separate tool compile your specialized syntactic sugar would be a problem.

4. **New AST node kinds**: keep JSX nodes in the AST and handle them in later passes. This can improve tooling fidelity but increases implementation complexity across compiler/typechecker/codegen and makes sandboxing harder to reason about.

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The luau parser supports parsing not just the AST but also the CST. Not having dedicated AST nodes for JSX would likely be problematic as we would lose necessary syntactic information required to for a clean round-trip

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

that's a good point. might be good to keep it in the AST, but still ultimately boils down to a function call in the CST. one goal is I didn't want to introduce new fundamental language features with this.

5. **Configurable factory** (future): allow `--!jsxFactory=...` or similar to avoid requiring `jsx` to be global; still requires a syntax proposal, and introduces additional complexity in binding resolution.
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You cannot really propose this as a future change because the world would be different if we did your current proposal and then this vs. if we just did this. If we just did this, there'd never exist the global, whereas if we do your current proposal, the global will exist forever and the only way to do this script directive would essentially be expanding it into assignment to that global.