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

Rewriting .ts module specifiers to .js without a plugin #2435

Open
bakkot opened this issue Aug 3, 2022 · 4 comments
Open

Rewriting .ts module specifiers to .js without a plugin #2435

bakkot opened this issue Aug 3, 2022 · 4 comments

Comments

@bakkot
Copy link

bakkot commented Aug 3, 2022

The TypeScript team seems set on making a decision I think is going to lead to a lot of pain for beginners: they are categorically against rewrite module specifiers. This means that beginners who are using tsc as a compiler are going to need to deal with the super hacky thing where you write import "foo.js" to refer to a file named foo.ts. I think that's going to cause a huge amount of confusion, over and above the existing pain the esm transition entails. It already is, really. (I was somewhat distressed, although not altogether surprised, to learn from the above-linked tweet that the hard part of building godbolt.org is apparently, somehow, JavaScript.)

But it looks like TypeScript might allow you to write .ts in module specifiers when using noEmit, relying on other tools to do that transformation for you. I'm hoping esbuild can be such a tool without needing a plugin.

I know esbuild is already capable of doing rewrites of module specifiers using a plugin, but the plugin API is a lot to ask a beginner to deal with. (Plus it requires enabling bundling even though you are actually not bundling at all, which is confusing in its own right.)

So: I'd like to ask for esbuild to support rewriting extensions in module specifiers without needing a plugin. That way we as a community would have a reasonable answer for a beginner setting out to do the should-be-straightforward task of writing a simple node.js application with TypeScript: 1) write module specifiers the way you expect to write them, i.e. with a .ts extension, 2) use tsc --noEmit --strict for type checking, 3) use esbuild --rewrite-ts (or whatever) to actually create .js files which you can run.

Again, I know it's possible to accomplish this with a plugin, but I'm concerned specifically about the experience for people new to JavaScript, trying to do something simple; I think a plugin is a bad match for that specific case. And again I realize this is a divergence from TypeScript, but that's precisely the point; TypeScript has decided that they are willing to sacrifice the experience of beginners, and I'm hoping esbuild might decide otherwise, so that as a community we can have at least one reasonable option for people just getting started.

@hyrious
Copy link

hyrious commented Aug 3, 2022

First of all, esbuild is not a transpiler like swc or babel at the first place, it is a bundler and it can already make sure multiple chunks could refer to each other using the right extension. REPL Link

Second, I guess you're asking esbuild to automatically transform external paths to replace .ts with .js? Like, if I have this input:

import "./lib.ts"

With esbuild --bundle --external:./b.ts --format=esm, you're expecting:

import "./lib.js"

Correct me if I'm wrong.

If that's true, then I don't think this would be any useful as normally people won't externalize one local file in this way. And again esbuild is not designed in the first place to transpile files like babel/swc/rollup. I can understand that some node loaders using esbuild would expect improving the transform api to do this work.

@bakkot
Copy link
Author

bakkot commented Aug 3, 2022

esbuild is not a transpiler like swc or babel at the first place

esbuild is in fact a transpiler? I'm not sure what claim you're making here. esbuild without --bundle will strip typescript types, and adding --format=cjs will translate esm to cjs.

I guess you're asking esbuild to automatically transform external paths to replace .ts with .js?

I'm asking about use without --bundle - i.e., the default behavior, which mostly just strips TypeScript types. So, yes, in a sense I'm asking about automatically transforming external paths, but where all paths are external.

If that's true, then I don't think this would be any useful as normally people won't externalize one local file in this way

If you have to name every individual file in --external, I agree people won't do that, but that's only necessary when using --bundle. What I'm asking about is the case where esbuild is just being used to strip types (and possibly convert esm to commonjs). This is already something people do, since esbuild is much faster than tsc for this application.

@Conaclos
Copy link

TypeScript 5.0 beta supports imports of files with .ts extension in bundler mode.

I could refine the proposal of bakkot as follows:

Change the extension of .ts to .js for imports that are part of entry points.
External imports should not be rewritten because a custom (TypeScript) loader may be used.

e.g.

$ more src/internal.ts
export * from "some/external.ts"

$ more src/index.ts
import { X } from "./internal.ts"

$ esbuild src/*.ts --outdir=dist
$ more dist/internal.js
export * from "some/external.ts"

$ more dist/index.js
import { X } from "./internal.js"

1zumii added a commit to 1zumii/vscode-starless-monokai that referenced this issue Oct 30, 2023
remove tsc for its compiling ESM path behavior is
leaving file specifier untouch
and force using import path .js instead of .ts
in mode: Node16, modeResolution: Node16
truly, modeResolution: commonjs can solve this confusing problem
but commonjs setting isn't recommend anymore

reference:
- typescriptlang.org/docs/handbook/modules/theory.html#the-module-output-format
- github.com/evanw/esbuild/issues/2435
@Conaclos
Copy link

Conaclos commented Oct 7, 2024

This may be worth reconsidering this feature request, as TypeScript 5.7 will ship with a new option to rewrite relative import with ts extension to relative import with js extension.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants