镜像站点 · 本页由第三方 GitHub 只读镜像提供,非 GitHub 官方站点,不接受任何登录或凭据输入。前往 github.com
Skip to content

How could we support typescript without vendoring it? #43818

Description

@mcollina

I would like Node.js to support the following user experience

$ node script.ts
Typescript support is missing, install it with:
npm i --location=global typescript-node-core
$ npm i --location=global typescript-node-core
...
$ node script.ts
"Hello World"

(I picked the typescript-node-core name at random, I would be extremely happy if that could be ts-node)

Note that script.ts could evaluate to either cjs or mjs depending on the tsconfig.json file.

Originally posted by @mcollina in #43408 (comment)


Why vendoring is not an option?

  1. TypeScript changes too fast and they do not follow semantic versioning, nor do they provide any long-term support.
  2. We will never be able to pick a default tsconfig given the 100+ options.

Activity

  1. changed the title [-]How could we support semi-native typescript without vendoring typescript?[/-] [+]How could we support semi-native typescript without vendoring it?[/+] on Jul 13, 2022
  2. mcollina commented on Jul 13, 2022

    @mcollina
    SponsorMemberAuthor
  3. targos commented on Jul 13, 2022

    @targos
    Member

    So basically you would like Node.js to automatically load something from a global predefined place when it starts?

  4. mcollina commented on Jul 13, 2022

    @mcollina
    SponsorMemberAuthor

    So basically you would like Node.js to automatically load something from a global predefined place when it starts?

    Yes, but only when started with a .ts file.

  5. mscdex commented on Jul 13, 2022

    @mscdex
    Contributor

    Wasn't that the main purpose of custom loaders? You should even be able to set that in a NODE_OPTIONS.

  6. GeoffreyBooth commented on Jul 13, 2022

    @GeoffreyBooth
    Member

    Please don't start issues related to loaders without tagging @nodejs/loaders

  7. mcollina commented on Jul 13, 2022

    @mcollina
    SponsorMemberAuthor

    This is not related to loaders. This is about the developer experience of Node.js.
    Loaders are custom, user-specified components that are started before the app.
    I'm talking about shipping something that would provide a better user experience for TypeScript users without additional configuration.

    Please keep this issue about the user experience and not specific implementations.

  8. removed
    loadersIssues and PRs related to ES module loaders.
    on Jul 13, 2022
  9. JakobJingleheimer commented on Jul 13, 2022

    @JakobJingleheimer
    Member

    I'm trying to see how this isn't reinventing the wheel, but I'm not getting it. Could you please explain how it's substantially different?

    How I'm seeing it is: This is already easily achieved via Loaders with next to no effort (and that effort is basically set-and-forget). We have a simple working example of it in node/loaders-tests/typescript-loader. For a more robust one, ts-node provides such a loader (ts-node/esm). And what we currently have via loaders is super fast. We just switched to it at my work and saw like an 800% speed improvement.

    PS In case we veer into the territory: I would vehemently object to TypeScript support in Node itself.

  10. mcollina commented on Jul 13, 2022

    @mcollina
    SponsorMemberAuthor

    I'm trying to see how this isn't reinventing the wheel, but I'm not getting it. Could you please explain how it's substantially different?

    I would rather stay away from discussing a specific implementation of this. This could be loaders but it doesn't matter. I care about listening to our users. Once we agree on the user experience, then we figure out what's the best way to ship it. If it's already possible as you hinted, it would be terrific.

    PS In case we veer into the territory: I would vehemently object to TypeScript support in Node itself.

    That's what our users are asking for. We cannot provide TS directly into Node.js core for plenty of reasons (it's just not possible for the tsconfig mayhem), including the fact that the TS team think it's a bad idea. I propose that we implement the user experience described in #43818 (comment).

  11. aduh95 commented on Jul 13, 2022

    @aduh95
    Contributor

    A few questions come to mind:

    • Are we making an exception for TS only, or are we open to adding more file extensions custom error message in the future?
    • How do we chose what tool to recommend?
    • Is Node.js core the correct place to solve that kind of problem?

    Currently, what happens when someone runs node script.ts depends on the env:

    • if we're inside a { "type": "module" } package, it will throw as .ts is not a recognized extension – in this case, it's quite easy to add a custom error instead as you're suggesting;
    • otherwise, it will parse script.ts as a CJS module – in this case, we could try to parse the file as a CJS module, and if that fails display the error message you're suggesting?

    We would then need a way to detect that typescript-node-core program is installed on the local env, and to defer to it to load that file. Maybe by making a $PATH lookup?

  12. GeoffreyBooth commented on Jul 13, 2022

    @GeoffreyBooth
    Member

    In an app with "type": "module" in its package.json, you get this:

    node script.ts
    node:internal/errors:477
        ErrorCaptureStackTrace(err);
        ^
    
    TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".ts" for /private/tmp/test/script.ts
        at new NodeError (node:internal/errors:388:5)
        at Object.getFileProtocolModuleFormat [as file:] (node:internal/modules/esm/get_format:80:11)
        at defaultGetFormat (node:internal/modules/esm/get_format:122:38)
        at defaultLoad (node:internal/modules/esm/load:21:20)
        at ESMLoader.load (node:internal/modules/esm/loader:431:26)
        at ESMLoader.moduleProvider (node:internal/modules/esm/loader:350:22)
        at new ModuleJob (node:internal/modules/esm/module_job:66:26)
        at #createModuleJob (node:internal/modules/esm/loader:369:17)
        at ESMLoader.getModuleJob (node:internal/modules/esm/loader:328:34)
        at async Promise.all (index 0) {
      code: 'ERR_UNKNOWN_FILE_EXTENSION'
    }

    So to provide the UX you’re describing, we would have to add a special case within this error flow where if the unknown extension is .ts, we print a special message. This will inevitably raise the question of what other unknown extensions do we want to print guides for; or we could print some message for all unknown extensions along the lines of “go to https://nodejs.org/guide-to-loading-non-javascript-file-types” and that documentation page could be a clearinghouse of instructions for various types.

    In an app without "type": "module", however, script.ts is parsed and executed as CommonJS. If it happens to be runnable JavaScript, it runs. Otherwise it’ll error when V8 tries to parse TypeScript syntax. So to provide a similar experience in CommonJS, within that error path you’d have to add a check that the file that couldn’t be parsed had an unknown extension, and then print the message. Keep in mind that there’s a type annotations proposal that would make many TypeScript files into runnable JavaScript.

    Either way, the solution that we would be recommending to users would involve adding the TypeScript loader. And transpiling TypeScript is squarely one of the use cases for loaders. So I object to treating this as “not related to loaders” and removing that label.

  13. aduh95 commented on Jul 13, 2022

    @aduh95
    Contributor

    @GeoffreyBooth let's imagine that the solution ends up looking like that (oversimplification):

    if (entryPoint.endsWith('.ts')) {
      process.argv.unshift(entryPoint);
      entryPoint = '~/.node/typescript-node-core/bin.js';
    }

    Sure typescript-node-core will probably use loaders, but that's arguably an implementation detail. (FWIW I agree that a clean solution should involve loaders; if anyone is interested to work on this, that's where I would suggest them to explore)

  14. JakobJingleheimer commented on Jul 13, 2022

    @JakobJingleheimer
    Member

    What if it were some kind of…plugin (idk what the official term is relative to Node.js), like crypto or intl, with which node could be compiled or not? We could then have however many and we aren't responsible for them. It would be very similar to loaders, but a bit different and i think perhaps a little closer to what Matteo is talking about.

    Also, this would avoid incorporating typescript-specifics (eg their .ts vs .js file extension nonsense) into node core (which is my main concern).

    Annnnd it wouldn't require the CLI args everyone laments about loaders.

  15. 75 remaining items

  16. knyga commented on Oct 12, 2022

    @knyga

    These days it is common to use services that do provisioning and management for you, AWS Lambda in particular. So they do not care that much about node.js runtime.
    But developers do care. Developers are forced to transpile the code before deployment and have transpiling pipeline.

    Secondly, I can't believe external transpilation could do better than native execution.

    Thirdly, bun and deno already have this feature. Their runtimes want to stand out and get some audience based on unsolved needs. Isn't it a good sign that market expects TypeScript to be there?

  17. eduardofx commented on Oct 21, 2022

    @eduardofx

    I think we can have pre-defined values of tsconfig and the developer who wants to use other resources create inside the folder manually.

  18. vdeturckheim commented on Feb 1, 2023

    @vdeturckheim
    Member

    Ok, I started to experiment with implementation and here are my findings/opinions for tonight:

    • the UX suggested by @mcollina works pretty well
     % ../node index.ts
    The TypeScript compiler is not available.
    Install it following documentation at https://nodejs.org/en/knowledge/getting-started/working-with-typescript/
    

    then

     % ../node index.ts
    1
    number
    

    if the @node/tsc-adapter module is available in the path - it can be local or global

    • I don't have strong opinions on which transpiler to use. We can even allow multiple
    • we might want to ship a very conservative default config (something that is expected to hopefully never change or break) and let users extend or override it
    • we should probably prevent loading a .ts file from node_modules or at least allow it behind a flag, this can't go well because of potential configurations missmatches
    • repl case will probably require a flag/entry in package.json if exist - that's probably ok - we probably might ignore TypeScript in the repl for the first iteration anyway?
    % docker run -it --init denoland/deno:1.10.3 repl
    WARNING: The requested image's platform (linux/amd64) does not match the detected host platform (linux/arm64/v8) and no specific platform was requested
    Deno 1.10.3
    exit using ctrl+d or close()
    > const x: number = 1
    Uncaught SyntaxError: Missing initializer in const declaration
    
    • we hit a similar limitation as Deno -> we need to have the relative modules have a .ts extension. I guess that since for 99.9% of the cases, semantically, TypeScriptTranspile(jscode) === jscode we could just ignore this problem?

    I won't have coding time for a couple days, but should go back into writing stuff early next week

  19. GeoffreyBooth commented on Feb 1, 2023

    @GeoffreyBooth
    Member

    @vdeturckheim This looks good! A few thoughts:

    • I don’t know what this proposed @node/tsc-adapter will contain, but I assume it’s a TypeScript loader like ts-node? If so it’s tied to the Loaders API which is still experimental, but Move ESM loaders off-thread #44710 is the last PR before it could become stable. Also this TypeScript support will surely be experimental for a while too, and longer than the Loaders API is, so that shouldn’t be an issue.
    • Since the @node/tsc-adapter package would be installed and versioned separately from the Node runtime, it would have to support a range of Node versions. I think this is a problem already solved by ts-node and similar projects.
    • Regarding package.json stuff, there was a prior discussion about including the loader settings (like what would be passed into --loader) in there, and I remember the consensus landing on including all of NODE_OPTIONS in there, or as much as is feasible. I think the way forward is to create something like a package.json "nodeOptions" field and add as many options as possible; it’s probably impractical to support all 100+ in a single PR, but we could at least come up with a design that can accommodate eventually supporting all of Node’s options and we can add them in batches.
    • Requiring the .ts extensions isn’t so weird, they’re already required in ESM JavaScript generally. I know it’s unidiomatic for TypeScript, but support for explicit .ts extensions is coming in TypeScript 5.0 and we can provide an informative error on missing .ts extension like we already do for missing .js extension.
  20. vdeturckheim commented on Feb 1, 2023

    @vdeturckheim
    Member

    Thanks @GeoffreyBooth , these are very good points!
    Yeah, @node/tsc-adapter is another name for typescript-node-core suggested in the first message of the thread and would be ts-node with node using it instead of having to plug into node. Basically, making the TS compiler live outside of node while allowing node xx.ts without any other argument. Right now, I made it work with cjs but for esm it will likely be a loader (we had experiments with @targos a few years ago on that too).

    Regarding node vs. package version, this is a very good point.
    I think we are going to a direction where the typescript support can work very well in most cases and where we provide DIY solutions for when it does not (like overriding the typescript adapter for instance).

    For repl and .ts cases. I agree 💯 with you. In the end, these are best-effort and DX choices, we can take the decisions and document them. Also, this is what experimental support is for!

    I missed last summer's summit (and this thread until recently fwiw), so I hope I am still going in the direction people agreed upon 👼

  21. GeoffreyBooth commented on Feb 2, 2023

    @GeoffreyBooth
    Member

    I missed last summer's summit (and this thread until recently fwiw), so I hope I am still going in the direction people agreed upon 👼

    Yes, I think nothing has changed from #43818 (comment) and what your recent experiments seem to assume. About the only “news” is that we’ve made so much progress with the Loaders API that I can see it applying to CommonJS too in the not-too-distant future; so I would prioritize getting TypeScript support via Loaders working first or at least ship that at the same time as CommonJS solution. @cspotcode and others have also had discussions about hooks in addition to those provided in the Loaders API, to do things like customize the REPL, for a more seamless DX beyond loading modules. Hopefully we can use the interest in better TypeScript support to build out some of those APIs.

    It sounds like package.json options aren’t needed for the core/MVP use case, so maybe we should get the initial to-do list done and then we can loop back to that and give it the attention it deserves.

  22. vdeturckheim commented on Feb 2, 2023

    @vdeturckheim
    Member

    This sounds good, thanks for the highlights! Seems good to me! I'll resume work with that in mind!

    Regarding the point in not treating "typescript" differently (in #43818 (comment)), I agree, that's one of the advantage of having something like @node/tsc-adapter which we control and would only serve the goal of calling "typescript" from node under the hood on .ts files. But nothing prevents to also have @node/swc-adapter or @node/esbuild-adapter tooto let the end user chose the transpiler in a list of provided alternatives (these packages are proxies to the other packages which also abstract breaking changes such packages could have). The end user will anyway be able to use a custom loader if they want to use something else in the end.

    But let's focus on the MVP for now and add too much complexity later 👼

  23. tony-go commented on Feb 2, 2023

    @tony-go
    Member

    But nothing prevents to also have @node/swc-adapter or @node/esbuild-adapter tooto let the end user chose the transpiler in a list of provided alternatives (these packages are proxies to the other packages which also abstract breaking changes such packages could have). The end user will anyway be able to use a custom loader if they want to use something else in the end.

    +1 on this ^^

  24. GeoffreyBooth commented on Feb 2, 2023

    @GeoffreyBooth
    Member

    But nothing prevents to also have @node/swc-adapter or @node/esbuild-adapter tooto let the end user chose the transpiler in a list of provided alternatives

    I’m not sure Node needs to publish a transpiler at all. Whatever docs page our error message links to could include a list of ones known to work, and encourage the user to choose one to install. I would think we should at least start with that approach, and if we find a need for publishing something official we could do that after MVP.

  25. hinell commented on Jul 2, 2023

    @hinell
  26. hinell commented on Jul 21, 2023

    @hinell

    There is a related proposal to add type syntax to JS, checkout:

  27. shinebayar-g commented on Dec 21, 2023

    @shinebayar-g

    What is the latest update on this? With both Deno and Bun improving their compatibility with Node.js and providing native support for TypeScript, there seem to be numerous examples and lessons to be learned from their advancements.

    Since it hasn't been mentioned in this thread, I would like to inquire whether this proposal could enable the distribution of npm packages exclusively written in TypeScript. Such a capability would significantly enhance the user experience when working with Node.js and TypeScript, eliminating the need to juggle between .d.ts and .js files.

  28. GeoffreyBooth commented on Dec 21, 2023

    @GeoffreyBooth
    Member

    What is the latest update on this?

    We’re exploring this space via #49704 (cc @JakobJingleheimer). One idea we’ve been considering lately is that Node could possibly provide some kind of blessed or automated way to install plugins, one of which could be TypeScript. Part of the problem is that TypeScript itself doesn’t provide Node integration, and there are many third-party plugins for that purpose, and we would want to avoid picking a winner. There’s also no one correct way to set up TypeScript; it depends on your build tool and its configuration, your app and its framework and those expectations, and so on.

    You might want to open an issue with the TypeScript team, if there isn’t one there already. They’re the ones who have the power to add official support for runtime transpilation and/or type-checking, and/or provide a blessed/recommended tsconfig.json for Node projects. TypeScript itself is somewhat coupled to Node, in that the extensionless resolution mimics Node’s CommonJS resolution and the conversion of ES modules to CommonJS modules was created to address former limitations of Node; the TypeScript team is perhaps the best source for how they advise integrating their tool with Node.

  29. AugustinMauroy commented on May 4, 2025

    @AugustinMauroy
    Member

    I think we can reasonably close this issue

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    discussIssues opened for discussion and feedback.

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions