parcel-bundler / parcel

The zero configuration build tool for the web. 📦🚀
https://parceljs.org
MIT License
43.48k stars 2.27k forks source link

Parcel 2: TypeScript automatic zero configuration not type checking or reading tsconfig.json #4022

Closed bbugh closed 4 months ago

bbugh commented 4 years ago

I've been following #1378 for a long time, and I'm thrilled to see Parcel 2 taking on better first-class support for TypeScript. There's currently some problems with Parcel 2 out of the box, and I didn't see any threads tracking this, so I'm making an issue.

🐛 bug report

In 2.0.0-alpha.3.2, given a very basic TypeScript project, there are three issues:

🎛 Configuration (.babelrc, package.json, cli command)

Here's all of the files:

🤔 Expected Behavior

As part of parcels "zero configuration", parcel automatically detects TypeScript and configures the project to correctly use TypeScript.

😯 Current Behavior

💁 Possible Solution

I'm guessing one of these two potential solutions:

  1. The current default transformer-babel is correctly configured to behave as expected with TypeScript, extending from #3083
  2. typescript and @parcel/transformer-typescript(-tsc?) is automatically installed when Parcel 2 detects a .ts file.

Either one is fine, I think, as long as the TypeScript compilation actually behaves like TypeScript by default!

🔦 Context

💻 Code Sample

See files above

🌍 Your Environment

Software Version(s)
Parcel 2.0.0-alpha.3.2
Node v10.15.3
npm/Yarn 1.21.1
Operating System Mac 10.14.6
mattrossman commented 4 years ago

@phaux Not that simple:

DeMoorJasper commented 4 years ago

This requires you to point to the compiled .js file in your HTML, rather than the source .ts entrypoint as is usually taught in the parcel guides, which adds in ambiguity (you need to know where that file will be compiled) and could throw off beginners.

I don't think there's any reason to make it this complicated, tsc will pick up the entrypoint regardless and Parcel will still do it's thing. I've done this on many projects and it's far from this complicated.

phaux commented 4 years ago

@mattrossman

  • This command assumes you have configured a .tsconfig file

Well, duh. This is how TypeScript works. Are you proposing that Parcel should ship it's own opinionated default tsconfig? Who decides which strict options are enabled, which DOM/ES libs you use, which target you want, where are your custom typings, etc?

  • This requires you to point to the compiled .js file in your HTML, rather than the source .ts entrypoint as is usually taught in the parcel guides

What? I think I don't understand. It's enough to just run tsc in the console. There's a field "include" in tsconfig but it's optional.

  • this doesn't help during development where live reloads are important. I would expect a solution to work with parcel index.html and automatically re-run type checking when my files change.

I'd say that being able to live reload an application and use it even when types are wrong is a good thing for quick prototyping. You see the errors in the IDE anyways and you can always run tsc from console or configure Parcel plugin if you want the whole project checked.

bbugh commented 4 years ago

I am honestly very confused about the parcel maintainers strong resistance to improving the language support!

I definitely understand where you're coming from about Babel with fast compilation. That is compelling and I think you're right that it's a good default. What I'm less clear on and haven't seen much discussion about is why there's no typing support at all by default. Parcel's main agenda seems to be bring "convention over configuration" to JavaScript land... except for TypeScript? If the instructions are always going to be "install typescript, run tsc --watch" why isn't that the default zero-config setting? Why do we need yet another terminal window for yet another command when it can be run in the bundler quite easily? 😳

With a couple of changes, Parcel could offer strong TypeScript support without losing the value that your implementation Babel brings. When a .ts file is imported, Parcel only needs to:

You still get the fast compilation with Babel and the fast development turnaround, but it operates more closely with the expectations of what "supporting typescript" really means. That's a significant improvement over what is happening now.

What do you think?

Well, duh. This is how TypeScript works. Are you proposing that Parcel should ship it's own opinionated default tsconfig? Who decides which strict options are enabled, which DOM/ES libs you use, which target you want, where are your custom typings, etc?

No, Parcel doesn't need to include an opinionated tsconfig.json because you can already generate the Microsoft opinionated default tsconfig tsc --init and it works fine with Parcel.

devongovett commented 4 years ago

I think I mostly feel like we cannot win here. Some users want Parcel to do type checking and some don’t. Parcel has never done type checking by default. Even in v1 you needed to install a plugin. I’m happy to enable it by default, but it just feels like we’ll get a bunch of issues asking us to turn it off then, especially if it isn’t a better experience than using TSC directly. 🤷🏻‍♂️

Regardless of the default, the TS validator plugin and the validation infrastructure in general is a bit under used atm and needs some serious work. We need to make sure it updates properly when files change, handles caching correctly, supports various TS config options, etc. Would someone on this thread be willing to help out with that?

We’d love to make using TS with parcel a great experience but I’m not sure anyone on the core team has the time or knowledge about how to make that happen at the moment so we would very much appreciate some help. 😍

astegmaier commented 4 years ago

@devongovett I know I've been AWOL for the last month, but improving the type-checker is still a long-term goal of mine. FWIW, I'm also in the "turn it on by default" camp, esp. once we iron out all the kinks. I'd love some help, if anyone on this thread is interested.

mikeplus64 commented 4 years ago

babel-transformer also does not seem to understand the as keyword in, like foo as Type. It is not accurate to call this TypeScript support; Parcel 2 is supporting whatever subset of it that babel-transformer happens to understand. I think this has the potential to be extremely frustrating to end users who expect TypeScript support but run into babel-transformer quirks; weighed up against that, requiring a tsconfig.json is really not so bad, especially since reasonable defaults can be generated straight from tsc. I mean, it's one of the first things that the TypeScript documentation states:

The presence of a tsconfig.json file in a directory indicates that the directory is the root of a TypeScript project. The tsconfig.json file specifies the root files and the compiler options required to compile the project

I may simply be mistaken about Parcel's design goals, but I took "zero-configuration" to mean not that there was literally zero configuration at all, but that you wouldn't have to configure Parcel itself. As in, if I wanted Parcel to bundle a Rust project I would expect to have to have a Cargo.toml somewhere; that's what a Rust project is. It's a pretty common pattern on the Parcel v1 docs for various languages for them to recommend or require making configuration specific to that language; like a .sassrc or .babelrc or bsconfig.json etc.. Likewise, if I am coding a TypeScript project, I expect to have a tsconfig.json--that's what a TypeScript project is.

Just my 2c. Strongly in favour of making tsc the default, as babel-transformer doesn't (and can't) actually support TypeScript, in its entirety.

I think whether tsc should yell at you with type errors when parcel invokes it is a separate question to whether tsc should be used for compilation. (Of course the answer is yes it should =), but it doesn't really matter to me since using my editor itself will yell at me for type errors as I go, and I suspect that would be the case for most people using TypeScript.) I thought the Parcel v1 pipeline of tsc->babel worked really well.

101arrowz commented 4 years ago

In any case, not validating types kind of defeats the point of TypeScript. To drive the point home: it's as if you didn't throw an error when a dependency wasn't found, and instead silently replace that dependency with null in the generated JS; it isn't a syntactic error but it absolutely should be validated. So here's my take:

If a tsconfig.json is present, compile with tsc by default. If not, use the standard JS pipeline but validate the types after each build. My reasoning is that those who don't even spend the time to create a tsconfig.json are just hacking together a prototype and will prefer faster builds over safer builds. I'd be willing to create a PR for this kind of system.

orta commented 4 years ago

Personally, I don't think a bundler should run the type checker - a bundler's responsibility is to remove the types from the TS and convert it to JS as fast as possible. Putting the TS type-checker in the way of that slows the critical path a lot and adds a bunch of extra complexity to parcel when you want to make it fast (result caching, incremental builds etc )

It's likely anyone using TypeScript would already have their editors set up to show them the errors inline, and running tsc from the command line is a perfectly normal final check for the whole project.

( To avoid adding to the noise in this thread, projects like svelte and vue have things like svelte-check and vti for the same purpose as tsc )

samhh commented 4 years ago

Often a change in one file causes a type error on the other side of the project. I can't imagine only running proper type-checking at the very end of development, particularly as someone who's very "type-driven".

bbugh commented 4 years ago

Also running tsc from the command line does not support .vue files, others have commented about it in this thread. That is not a workable solution.

The only way to type check Vue is to build it into the build process in some way. Given that Vue 3 is in beta-almost-RC and it was rewritten in TypeScript and now has much better TS support, it's pretty important.

argv-minus-one commented 4 years ago

@orta I'm going to have to completely disagree with you. A build that is a bit sluggish but has correct results is merely inconvenient. A build that is fast but has incorrect results is useless. If I wanted “as fast as possible” and didn't care if my code is correct, I wouldn't be using TypeScript.

It's likely anyone using TypeScript would already have their editors set up to show them the errors inline

Editors cannot show all TypeScript errors. Editors also cannot show errors that occur in files other than the one being edited.

running tsc from the command line is a perfectly normal final check for the whole project.

No, it's not. Running your build system from the command line is a perfectly normal final check for the whole project. In a project using Parcel, that build system is Parcel, not tsc.

orta commented 4 years ago

I get that I'm posting in a thread that can realistically only be made up of people who disagree, but TypeScript always emits JS on purpose and has a compiler flag which you can turn on to get the workflow you're talking about ( noEmitOnError) however the default (and how it works with tsc) is to not block JS emit on TypeScript errors.

samhh commented 4 years ago

Unless I'm misunderstanding I don't believe that's relevant, you'll still get type-checking. I personally don't care if it emits or not upon type-checking failing, only that there is type-checking and any errors are reported to me.

devongovett commented 4 years ago

Based on the number of people on this thread, it seems supporting TS better is something we should work on. If I were to put together a plan for how we could accomplish the goals of this thread, would anyone be willing to help us implement it? There’s a lot more to this than just a config change, eg .vue/.svelte TS checking support is a big project on its own and not something that’s currently supported even with the existing plugins.

101arrowz commented 4 years ago

I'd be willing to help improve TypeScript support, especially with regards to Vue. In the Parcel 2 Vue transformer, TypeScript files are actually imported as separate assets from .vue, which means that type-checking should be possible if the TS transformer could type-check.

DeMoorJasper commented 4 years ago

@101arrowz typechecking does not and should not happen in a transformer, it will significantly slow down building. It happens in the typescript validator https://github.com/parcel-bundler/parcel/tree/v2/packages/validators/typescript

There's a lot more to typechecking in a tool like this than just calling tsc.

101arrowz commented 4 years ago

I'm not happy with performance being put ahead of safety, but since one of Parcel's core principles is being "blazing-fast," I suppose there really isn't any other option. My point is that TypeScript users, by nature of using TS over JS, have agreed to give up the rapid prototyping and monkeypatching capabilities of pure JS for more safety, and Parcel treating TS the same as JS might help prototyping but will make development more frustrating later on.

Regardless of the position the Parcel team chooses to take, the existing TS infrastructure could be improved upon, as you have mentioned, so I'll work on that for now.

devongovett commented 4 years ago

The reason for not doing type checking in the transformer isn’t performance, but because it’s actually impossible to do it there. Type checking requires a full graph of files. It cannot be done just on a single TS file at a time. For example, you could have imported and exported types and values. Transformer plugins in Parcel operate on individual files, as they are discovered. Type checking must be done in a Validator plugin, at the end, once the full graph is known.

devongovett commented 4 years ago

In order to support .vue type checking, we would need to store the TS asset that’s extracted by the transformer and somehow mark that for validation later. In the TS validator, when a .vue file is imported, we’d need to resolve that to just the TS part so that the TS compiler doesn’t need to worry about the other parts.

devongovett commented 4 years ago

This thread is getting very long and there's lots of different features being requested here, so I've put together three separate issues to discuss them:

  1. 4936 - Implement TypeScript resolver plugin. This covers the TS specific resolution requests for baseUrl, paths, and rootDirs.

  2. 4938 - Switch to TSC transformer instead of Babel by default. This is a simple one, but I'd like someone with a reasonably large TS project to do some perf measurements to ensure we are making the right decision. The alternative, as @bbugh said in the original issue, is to match the TSC config using Babel. This would be a short term solution that should help with some of the issues on this thread, e.g. class properties, decorators, and other features TS enables but Babel does not.

  3. 4937 - An RFC for service plugins. This would allow the TypeScript compiler to be run as a service in a dedicated worker that other plugins could make requests to. This is required to fully support all TS features, including things like const enums which require type information to compile and aren't supported by TSC's transpileModule or Babel. In addition, type errors for .vue/.svelte files could be implemented this way. This is a much longer term solution that we can begin working toward, and once it's ready, it could potentially become the default.

Please leave your feedback for the relevant feature on these individual issues. If anyone is interested in helping to implement them, please let us know. It would be greatly appreciated. 😄

TroySchmidt commented 4 years ago

I am happy to see there are issues now to improve the TypeScript support. The lack of TypeScript full adoption in Parcel has been a barrier to entry for us.
I have seen time and again people being either JavaScript or TypeScript people and neither understands the other side. If you truly use TypeScript you use it for the IDE intellisense and linting. But you also rely on the build failing when there are typing issues as those lead to edge and corner case bugs. I also totally understand the decision to use Babel for transpiling and performance. I also think a fourth issue to add is rich documentation on how to add TypeScript support using Parcel v2 and the options you have like using the validator and what the functionality and purpose that supports. There is also adding ESLint functionality that fully supports TypeScript as well.

I did add the validator and that does break when running code in the "debug" mode of Parcel.

kuba-orlik commented 4 years ago

For me lack of error checking on the Parcel side makes Parcel useless to use during development. The perfect use case is that Parcel watches the files, and rebundles them when changes are detected. Errors are shown in the terminal output if any are found.

Without that functionality I have to run Typescript checker and Parcel bundler at the same time, and my PC begins to struggle

TroySchmidt commented 4 years ago

@kuba-orlik So the initial start of parcel --open with the validator setup would error on a TypeScript problem. Also having ESLint properly setup with your IDE of choice (VS Code here) it highlights the issue. When building the validator kicks out an error and fails the build. The only time the build isn't errored out is on hot reloading. So, @devongovett would adding the validator to the hot reloading be part of #4937 or something that could be accomplished sooner? I also imagine it needs to take into account all validators that could error otherwise. Considering how a setup of validator + ESLint works that is enough type checking and build failing for me to consider Parcel2 a comparable bundler to the CRA \ webpack setup. The tooling could improve and the issues above I think are seeking to close that gap.

EDIT: Just discovered that it still doesn't support 'baseUrl' so without that Parcel just has too many oddities to not be able to use it with TypeScript projects. It supports it but only in a minimal form.

kuba-orlik commented 4 years ago

For my particular setup it would be enough if:

paeolo commented 4 years ago

For contrast, I've been very happy with Next.js's out-of-the-box Typescript support. Just rename a file to .tsx, and you are basically good to go. Next automatically adds a tsconfig.json if it doesn't exist.

I'm not sure if it uses tsc or Babel — either way it's working very well.

Well, I came to this thread with I think, the very same great experience in mind: Typescript support in Next.js is good. So I was convinced there was type-checking in Next.js at every-level, as my day-to-day experience with it suggests.

I was wrong.

Next.js uses webpack with babel-plugin-transform-typescript and its caveats. So when you run Next.js in developpement mode, where things goes only through webpack, there is no type-checking and you can verify for yourself with:

console.log(props.DOES_NOT_EXISTS);

On the other hand if you look at where the build code flows, you will find verifyTypeScriptSetup with this code:

// Verify the project passes type-checking before we go to webpack phase: return await runTypeCheck(ts, dir, tsConfigPath)

Some users want Parcel to do type checking and some don’t.

At the beginning I was in favor of it because of my experience with Next.js. But it's clear that Next.js, a kind of "zero-config" project, doesn't have it when in developpement mode (and it doesn't bother me) but have it when it comes to the build (and I think it's great).

So at the end of the day runTypeCheck (or whatever) && parcel build does the trick for me.

No, it's not. Running your build system from the command line is a perfectly normal final check for the whole project. In a project using Parcel, that build system is Parcel, not tsc.

I also agree with that, oh men.

incerta commented 4 years ago

If someone just simply seeks a quick TypeScript + Parcel + Jest (with allowed .js files import) solution example:

npm i -D jest typescript ts-jest @types/jest babel-jest @babel/core @babel/preset-env

Add .babelrc.js

module.exports = {
  presets: ['@babel/preset-env'],
}

Add jest.config.js

module.exports = {
  transform: {
    '^.+\\.(tsx|ts)?$': 'ts-jest',
    '^.+\\.[t|j]sx?$': 'babel-jest',
  },
}

Add tsconfig.json

{
  {
    "compilerOptions": {
      "module": "commonjs",
      "allowJs": true,
      "strict": true,
      "noEmit": true,
    },
    "include": ["src/**/*.ts"]
  }
}

From package.json

...
  "scripts": {
    "start": "parcel serve ./src/index.html --port 3000",
    "build": "tsc --noEmit && jest && parcel build ./src/index.html",
    "type-check": "tsc --noEmit --watch",
    "test": "jest --watch"
  },
...
LukeTOBrien commented 3 years ago

Hello there,

I think you've goon off topic abit, let me bring it back.

The point I make in my issue:

What % of TypeScript projects use a tsconfig.json?
I use a tsconfig in 100% of my TypeScript project, so in my case at least I am always going to manually configure to use tsc (which I never did in Parcel 1, it just worked)

Also:

for most cases developers will want to transform TypeScript as nature intended using tsc.

I think Parcel 2 should work the same as Parcel 1 with regard to TypeScript.
The reason that Babel is used is that it is a bit faster, but I am not concerned with speed, Parcel is fast enough, and TBH even if it takes seconds or minutes then I don't mind.

LukeTOBrien commented 3 years ago

@mischnic I do like some of the quotes above:

I do hope the developers listen and will switch to tsc by default.

mischnic commented 3 years ago

I think Parcel 2 should work the same as Parcel 1 with regard to TypeScript.

Parcel 1 didn't do typechecking either.

LukeTOBrien commented 3 years ago

I did notice that, but Parcel 1 used the tsconfig.
How does Parcel 2 handle React?... I do not use React myself but the Parcel 1 docs show you need a option in tsconfig.
Presumably using React in Parcel 2 would require the tsc config in the .rc?

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react"
  }
}
mischnic commented 3 years ago

The default Babel config generated by Parcel 2 would include @babel/preset-env, @babel/preset-typescript, @babel/preset-react in that case.

LukeTOBrien commented 3 years ago

I have created the .parcelrc config required for using TypeScript... I had two further errors because Parcel couldn't find the modules @parcel/transformer-typescript-tsc and typescript, so I had to npm i thouse.
I would say this is a breaking change from anyone who used Parcel 1 and it breaks the 'no congfiguration' pholisophy.

devongovett commented 3 years ago

You are welcome to submit a PR. Further complaining about this issue is pointless. We've created the issues linked in my above comment to make our intent clear. If you would like to help make that happen faster, please do.

devongovett commented 4 months ago

At this point, I'm going to close this. Parcel is not a type checker and will probably never be one. I don't have the bandwidth to implement and maintain this but if someone wants to implement it as a third party plugin that would be great (I guess it could probably be implemented as a reporter). Otherwise, I recommend simply running tsc --noEmit during your production build script and using TypeScript in your IDE during development.