-
Notifications
You must be signed in to change notification settings - Fork 31.2k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
JSX Support #56822
Comments
I was exploring this idea. module.registerJsxHook((obj) =>{
React.createElement(// logic to convert object into react)
}) <h1>Hello world</h1> Is transpiled into: process.getBuiltInModule('module').jsxHandler({
// The object transformed
}) |
What happens when there's more than one hook registered in an application, as would be virtually guaranteed to happen? There's a reason jsx isn't in the language yet, or in any platform. |
Care to explain why?
Expand pls |
What I mean is, the rules for the syntax are pretty universal and clear - the problem is that there's a plethora of transformations of it. If you had to pick only one, you'd pick React's - but which one? The pre-17 one, that's way more widely used, or the 17+ one, that's the future of React? If you only pick one, then you ace out anyone using an alternative transformation. If you allow it to be customizable, and it's not customizable per module (which would require syntax, for ESM, and thus TC39 support - although with CJS you could do it by just injecting a new function into scope), then you've created a global state and capability that means that any code that can set it, can override the meaning of what appears to be syntax - which is a massive security hole and attack vector. In other words, I think that the only way to support jsx that's even remotely reasonable here is with a loader, and users can already use custom loaders to achieve it - so "node supports jsx" would just mean that node ships a loader by default, which would mean node is picking a winner - and, if node's picking a winner, per the usage data, it's going to be React, and that's not exactly fair to React competitors. There may come a time in the future when the JS language can answer some of these questions - or when userland has largely settled on a single jsx transformation - but until that time, it feels at best incredibly risky and worst the precursor to an abject disaster for node to attempt to address this itself. |
@ljharb I understand the technical issues, but I don't agree with marking a problem as "won't fix" without exploring it. This has been one major historical problem of the loaders space, which has burned out several people. While I understand its not an easy fix, it would be more useful to talk about blockers and requirements for this to happen. The jsx support is something that can be fixed, just like typescript support, and we shoud look into fixing it |
A single transformation target is not what JSX needs. Any attempt to standardize it into a single format seems prone to failure. JSX community has already proven different syntaxes are better for different use cases:
function template(React) {
return <div></div>
} And that's part of how JSX is supposed to be. |
exactly. And when there’s multiple valid ways of handling a syntax, the existing solution for that is “custom loaders”. |
Those arguments are 100% applicable to TypeScript support too. But, Node.js team finally decided to choose one option (SWC in amaro) and implement basic support level keeping hooks only for advanced scenarios. What is the difference between JSX and TS? Both can be compiled by various ways, both can have overcomplicated external configuration, both are tightly coupled with 3rd-party ecosystem, etc. JSX / TS / support in Node.js just a step towards the community, because base framework functionality more than enough to implement any possible solution by using third-party tools / libraries / even native addons. What I want to say - JSX support is mostly not a technical question. May be it's need to have TSC decision or something like that. |
I'm not aware of an alternative semantic meaning for compiled TS output - it's JavaScript. Babel, tsc, swc, all compile TS with different approaches that result in the same runtime JS. JSX is vastly different, and the various approaches result in widely varying runtime behavior. |
it is possible to standardize the transpilation to a certain point and have the framework hook in with some Node API. We probably should ask framework maintainers. For example:
is transpiled to:
The framework can register a hook to do something with the object that is passed: |
It's not quite true - generated code is different even for the one particular tool. For example, tsc has a lot of flags that directly affect JS output: preserveConstEnums, downlevelIteration, importHelpers, noUncheckedSideEffectImports, esModuleInterop, verbatimModuleSyntax, importsNotUsedAsValues, preserveValueImports, etc. SWC has even more. This is why current state of TS support in Node is a compromise with some defaults and this is why we have 'Full TypeScript support' chapter in the documentation. |
That is indeed how it could technically be achieved - but then we've got global state (the current JSX handler) and apps could have multiple JSX transpilations in the same application, even ignoring third party dependencies. |
it's neither better nor worse than hooks you proposed - exactly the same behavior. But if you want to discuss technical details - jsxHandler can accept callback(filename) to return compilation options conditionally, depends on framework configuration. |
Thing is, it will always require source-maps, therefore always be available behind flag |
@koshic a custom userland loader can choose when to selectively apply a given transformation. @marco-ippolito if it's always available behind a flag, and it never applies to third-party code, then certainly a lot of the risks i've discussed are mitigated or don't apply, but I'm still concerned about it (especially about the impact it could have on future standardization of jsx syntax) |
We should probably ask for comment from framework authors |
Aside from the problems raised by @ljharb, I doubt this is going to be useful beyond simple use cases. From a meta-framework perspective (e.g. Next / Nuxt), the framework would prefer complete control over how JSX is compiled for their specific use cases and will most likely opt-out even this is supported in Node. From a perf perspective, frameworks also want to transpile JSX ahead of time during build instead of paying the transform cost at runtime bootup. From a design perspective, the fundamental issue I see is that:
Node's existing TS support suffers from similar problems IMO, but less critical due to limited permutations of options that affect transform output. Transpiling to
|
@marco-ippolito And IMO JSX sould be handle by customization hooks. Maybe have official one. |
JSX seems an obvious next step after finishing TS support in node when talking about integrating existing syntax into node. It might not fill enough edge cases to make a big framework blindly change to it but there are still use cases for JSX that are present in a lot of tools like https://jsx.email. When compared to complete TS support In nodejs, this comes as a very low priority but I think its not something we can ignore. |
What is the problem this feature will solve?
Following up on nodejs/node#56322 and nodejs/node#56392.
JSX and its various flavors have undergone frequent configuration changes since their inception. While supporting JSX without any configuration might seem ideal, it is both impractical and problematic. Just as
"type": "module"
was introduced to address CommonJS/ESM interoperability, we must continue relying on existing configuration files—tsconfig.json
orjsconfig.json
—to determine the correct transformation to apply.The presence or absence of
JSX
should not rely on node's recent work for supporting typescript.JSX has multiple targets, primarily
react
andreact-jsx
, which produce different outputs from the same input. For example:react-jsx
react
OutputjsxFactory
– Specifies the function to replacecreateElement
when using thereact
target. For example, settingjsxFactory: 'h'
replacesReact.createElement
withh
.jsxFragmentFactory
– Determines the function used for fragments (<></>
). If set, fragments will use the specified name instead ofReact.Fragment
.jsxImportSource
– Used with thereact-jsx
target to change the import source. For example, settingjsxImportSource: 'preact'
results in:Their combination is what is needed for any library that supports JSX, such as Vue.js, Kita, React, Preact, Astro, and others...
Given the diversity of JavaScript frameworks, adopting a default configuration favoring any single framework would inevitably create issues for others.
One alternative is to add fields to
package.json
to define some JSX settings, eliminating the need for ajsconfig.json
ortsconfig.json
file. However, rewriting this (ideal or not) standard is not something the community will like.In the long run, adding support for a native JSX transform function, similar to Deno’s implementation, could significantly enhance Node.js performance in SSR scenarios.
Deno's native transform has demonstrated 7-20× faster rendering times and 50% reduction in garbage collection overhead. Such improvements would benefit SSR frameworks like Next.js, positioning Node.js competitively once again.
What is the feature you are proposing to solve the problem?
I propose that nodejs transform JSX syntax and support
.jsx
and.tsx
files.What alternatives have you considered?
No response
The text was updated successfully, but these errors were encountered: