Skip to content
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

Support .tsx file extensions. #56322

Closed
cpojer opened this issue Dec 19, 2024 · 25 comments
Closed

Support .tsx file extensions. #56322

cpojer opened this issue Dec 19, 2024 · 25 comments
Labels
feature request Issues that request new features to be added to Node.js.

Comments

@cpojer
Copy link

cpojer commented Dec 19, 2024

What is the problem this feature will solve?

Node's --experimental-strip-types and --experimental-transform-types are great improvements that allow us to drop a lot of tooling to run TypeScript in node. Right now, it only supports the .ts extension and does not work on .tsx. .tsx only really exists because of syntax ambiguity with a (somewhat legacy) way of typecasting in .ts files: microsoft/TypeScript#26489 (comment)

Why would you use .tsx for backend code?

In a monorepo, you might have many packages shared by the client and backend. For consistency, those projects may want to use only a single file extension like .tsx. If you use React, which many people do, and some files use .ts and some .tsx, it's always jarring to type JSX in a .ts file, realize it's a syntax error, and then having to rename the file. The renames also make it harder to navigate git history. Finally, if you are using only a single extension, TypeScript's behavior is always the same with regards to how angle brackets work.

I'd be excited to implement this feature and send a Pull Request if the Node.js team is in favor of supporting .tsx extensions.

What is the feature you are proposing to solve the problem?

I imagine the most workable solution is to support .tsx extensions just like .ts, but throw an error if JSX is actually used. I'm not advocating for supporting JSX, just for node to support .tsx files without compiling JSX.

What alternatives have you considered?

The alternatives are to keep using third-party tooling to strip TypeScript types, or to rename all backend files using .tsx to .ts, which may not be possible in large monorepos given the amount of files affected, and as mentioned previously, makes git history more cumbersome to work through.

@cpojer cpojer added the feature request Issues that request new features to be added to Node.js. label Dec 19, 2024
@github-project-automation github-project-automation bot moved this to Awaiting Triage in Node.js feature requests Dec 19, 2024
@aduh95
Copy link
Contributor

aduh95 commented Dec 20, 2024

Would you like to send a PR?

@cpojer
Copy link
Author

cpojer commented Dec 20, 2024

Before working on it, I would like to discuss and reach consensus that the Node.js maintainers would like to include this feature.

@aduh95
Copy link
Contributor

aduh95 commented Dec 20, 2024

I'm not sure what there is to discuss, but in any case, it won't happen until someone submits a PR.

//cc @nodejs/typescript

@cpojer
Copy link
Author

cpojer commented Dec 20, 2024

I see, so from my perspective here is what I think we need consensus on:

  • Does Node.js want to support .tsx?
  • If yes, is it acceptable (at least as a first step), to support the .tsx extension only, without actually supporting JSX? See https://www.typescriptlang.org/tsconfig/#jsx for the complexity involved with supporting JSX.

imho this would solve the outlined problem for most users, but some might have a desire to support JSX directly in the future in some form.

@JakobJingleheimer
Copy link
Member

Many of the arguments cited in favour seem illogical to me. It seems like you're suggesting supporting a mismatched file extension and then erroring when the file extension is used correctly?

Supporting actual TSX would be quite a hairy ordeal, and encouraging people to misuse the .tsx extension seems like a bad idea.

Or have I misunderstood you?

@cpojer
Copy link
Author

cpojer commented Dec 20, 2024

Yes, you misunderstood. Using the .tsx extension without using JSX is not a misuse of TypeScript. Why would it be?

@JakobJingleheimer
Copy link
Member

JakobJingleheimer commented Dec 20, 2024

It seems I did not. That is a misuse of the file extension, and it would be a landmine for users. We should absolutely not do that.

However, if we were to support jsx and thus tsx, that would be a different story. One would need to figure out how to handle configuration, because jsx has multiple transpile options. I could see value in that, but I would advise first sketching out some broad-strokes before jumping to a PR (which would almost certainly result in contradicting opinions). Far better to get those to consensus before churning on an implementation. Happy to answer questions for that 🙂 I don't know the appetite of others (I'm not sure of my own opinion on it) to support jsx though.

@ljharb
Copy link
Member

ljharb commented Dec 20, 2024

How is it not a misuse of a file extension to do that? That's what .jsx exists for, for when you have jsx and only when you have jsx, and I'd expect ts/tsx to be the same thing. If there's no jsx in it, it's just JS or TS, and the file extension should reflect that accurately.

@cpojer
Copy link
Author

cpojer commented Dec 20, 2024

There are many projects with files that use the .tsx extension without using JSX. Tools like ts-node support it and TypeScript does not throw an error if it cannot find a JSX AST node in a .tsx file. It seems to me like .tsx is well-supported by TypeScript unlike using a random extension like .definitelynottypescript. Maybe I'm missing something, but do you have a source where the TypeScript team states it is a misuse of the extension if not JSX nodes are present?

On the topic of partial support, Node.js implemented --experimental-strip-types without support for TypeScript features like enums until --experimental-transform-types was added. Why can we not take a similar path for this?

@GeoffreyBooth
Copy link
Member

GeoffreyBooth commented Dec 20, 2024

It seems to me that supporting files with a .tsx extension but erroring on JSX syntax within would seem very broken to users. Sure it’s possible to write a .tsx file with no JSX syntax, just as it’s possible to write a .ts file with no TypeScript syntax, but I don’t think that’s what most users would consider “.tsx support.”

On the topic of partial support, Node.js implemented –experimental-strip-types without support for TypeScript features like enums until –experimental-transform-types was added. Why can we not take a similar path for this?

This proposal is more like saying “why not support the .ts extension but only if such files contain only JavaScript syntax.”

@marco-ippolito
Copy link
Member

marco-ippolito commented Dec 20, 2024

I explored this idea but I'm against it.
The main idea behind TypeScript support in Node is the type stripping. tsx cannot be run once types are stripped or syntax is transformed, it always requires an external loader.
Maybe, (maaaaaaybe) adding support in module.stripTypeScriptTypes would be "acceptable".

@cpojer
Copy link
Author

cpojer commented Dec 20, 2024

@marco-ippolito:

tsx cannot be run once types are stripped or syntax is transformed, it always requires an external loader.

Would you mind elaborating on this sentence?

@marco-ippolito
Copy link
Member

marco-ippolito commented Dec 20, 2024

@marco-ippolito:

tsx cannot be run once types are stripped or syntax is transformed, it always requires an external loader.

Would you mind elaborating on this sentence?

If types are removed, all its left is jsx that cannot be run without an external loader or a framework (that uses a loader)

@arthurfiorette
Copy link

The logical approach, considering the previously discussed --experimental-strip-types and --experimental-transform-types flags, is to introduce a new one: --experimental-jsx. This flag should include support for <jsx /> syntax and only it. Alongside it, just add .tsx as a valid ts extension and only strip types out of it,

While I agree with adding support for .tsx files without JSX, this "feature" should only be merged once Node.js can reliably execute JSX code.

Introducing a new flag might seem exhaustive, but it aligns with the established decision-making process. Moreover, the JSX implementation should remain independent of TypeScript-specific features, much like how .jsx files can be used alongside a jsconfig.json file.

This involves two distinct steps:

  1. Process .tsx files just as .ts files to strip their types.
  2. Properly handle .js and .jsx files that include <jsx /> syntax.

Providing only one of these features without the other is both counterintuitive and likely to confuse users.

@arthurfiorette
Copy link

arthurfiorette commented Dec 20, 2024

A potential first step toward this approach could be to simply ignore <jsx /> syntax entirely while adding support for the .tsx extension.

If this change is released with a clear note explaining that it currently ignores <jsx /> syntax, it could also recommend Node.js users rely on a custom loader like @educandu/node-jsx-loader for handling JSX in the interim.

This would provide a practical and usable solution much earlier, giving developers a path forward without having to wait for the complete implementation of a --experimental-jsx flag. That flag, once introduced, could be entirely independent of the Node+TypeScript-specific features. It would focus solely on transforming JSX syntax, regardless of whether TypeScript is in use.

@marco-ippolito
Copy link
Member

I'd first try to get consensus for jsx support, if that happens adding type stripping to tsx is easy.

@arthurfiorette
Copy link

The opposite would allow jsx usage via custom loaders, waiting for jsx support means no jsx at all until that + it would bring more people to use --experimental-strip-types and --experimental-transform-types which I think is better.

@GeoffreyBooth
Copy link
Member

JSX has (at least) two issues that would need addressing:

  1. It requires transformation, like transform-types.
  2. The transformation requires configuration, for example to specify React or Vue or something else.

JSX isn’t only used by React. For example, this JSX:

export default function Test() {
  return (
    <h1>Hello World!</h1>
  )
}

compiles per this site into this JavaScript for React:

export default function Test() {
    return /*#__PURE__*/ React.createElement("h1", null, "Hello World!");
}

and this for Vue:

import { createVNode as _createVNode, createTextVNode as _createTextVNode } from "vue";
export default function Test() {
  return _createVNode("h1", null, [_createTextVNode("Hello World!")]);
}

@arthurfiorette
Copy link

JSX indeed has specific targets, primarily react and react-jsx, with distinct output patterns for the same JSX input. For example:

<h1>Hello world</h1>

react-jsx:

import { jsx as _jsx } from "react/jsx-runtime";
_jsx("h1", { children: "Hello world" });

react:

import React from "react";
React.createElement("h1", null, "Hello world");
  • jsxFactory:
    Specifies the function to replace createElement when the react target is used. For example, setting jsxFactory: 'h' changes React.createElement to h.

  • jsxFragmentFactory:
    Determines the component used for fragments (<></>). If set, fragments will use the specified name instead of React.Fragment.

  • jsxImportSource:
    Used with the react-jsx target to change the imported package. For example, jsxImportSource: 'preact' would generate:

    import { jsx as _jsx } from "preact/jsx-runtime";

The combination of them is what is done for any library that supports JSX, such as Vue.js, Kita, React, Preact, or Astro.

@ljharb
Copy link
Member

ljharb commented Dec 20, 2024

It seems great to support jsx syntax somehow, but the closest "default" transformation of jsx is for react, and i'm not sure that's what everyone would want.

As for

For consistency, those projects may want to use only a single file extension

I think this is just an incorrect "want" - it's not like their images and JSON files are going to use the same extension, because the point of a file extension is 0% aesthetics and 100% accurately conveying the parse goal of the file.

@JakobJingleheimer
Copy link
Member

The opposite would allow jsx usage via custom loaders, waiting for jsx support means no jsx at all until that + it would bring more people to use --experimental-strip-types and --experimental-transform-types which I think is better.

This exists. @nodejs-loaders/tsx, ts-node, and others.

This discussion is going in circles. You are not addressing the problems this proposal creates, despite us explaining them in multiple different ways.

TS cavorting as TSX is a hard "no" by many. In the interest of sparing valuable time: I will block this (as others have indicated they will too).

JSX support is a maybe, dependent on design. I am happy to help you outline a possible plan forward, although I do not necessarily support it—I share Marco's concerns: the current implementation is brilliant in its simplicity, and this seems contrary and counter to that.

If you wish to pursue supporting JSX, please open a new issue. If you would like my help with that, please tag me.

@JakobJingleheimer JakobJingleheimer closed this as not planned Won't fix, can't repro, duplicate, stale Dec 20, 2024
@sibelius
Copy link

what is the workaround for this right now ?

@JakobJingleheimer
Copy link
Member

JakobJingleheimer commented Dec 29, 2024

Please see the comment immediately above 🙂

@sibelius
Copy link

@nodejs-loaders/tsx is the best option right now ?

@JakobJingleheimer
Copy link
Member

I'm the author of @nodejs-loaders/tsx, so I'm biased. AFAIK, I created nodejs-loaders (the previous incarnation of the @nodejs-loaders collection) before ts-node had a loader. I haven't used ts-node because @nodejs-loaders/tsx worked great for me.

@ljharb ljharb marked this as a duplicate of #56391 Dec 29, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
feature request Issues that request new features to be added to Node.js.
Projects
Archived in project
Development

No branches or pull requests

8 participants