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

Proposal: add native built-in typescript support #1859

Closed
brian-mann opened this issue Jun 3, 2018 · 36 comments · Fixed by #5906
Closed

Proposal: add native built-in typescript support #1859

brian-mann opened this issue Jun 3, 2018 · 36 comments · Fixed by #5906

Comments

@brian-mann
Copy link
Member

brian-mann commented Jun 3, 2018

Overview

In 3.0.0 we had to include a dependency that automatically bundled in the typescript binary. This added some significant weight to the overall app size (35mb) which begs the question- why not just provide native support for typescript? We originally decided not to do this because of the added weight. Now that's no longer a reason. Additionally, support for typescript has continuously expanded, and as part of our overall philosophy of being zero-config, I think it's time to add support for it out of the box. We already have several dedicated doc guides for this.

As we're coming closer to bridging the gap between the browser + the node context shifts, it means that we also need to think about the plugin support for both of these contexts.

For instance we have file:preprocessor event, but this is really only for spec + support files going to the browser.

What if users want to write their plugins/index.js file using newer node features that the current version doesn't support? What if they want to write that file in typescript? What if they want to include or require other node files or lib code that also needs this?

Proposal

  • Bundle in typescript as an official dependency in Cypress.
  • Automatically parse all .ts file extensions for node files found in plugins, etc
  • Automatically serve spec files with .ts extension
  • Automatically find/use the same .tsconfig that your project would be using in node normally, but with a preference for .tsconfig files in cypress folder.
  • Likely utilize ts-node to do typescript compilation on demand / in memory. Potentially do either transpileOnly or leave full blown type checking on.

Questions

  • What if the bundled version of Typescript is newer/older than the one you have in your project? We should probably automatically try to require the one built into your project, and if none is installed fall back to ours.
  • Perhaps we could accept a flag in cypress.json that prefers the bundled/native version of typescript even if a system/project typescript dependency was found
  • How would users modify the ts-node options we use by default such as transpileOnly? We could add more cypress.json configuration options but then we're back to preferring static configuration vs configuration with code.

Concerns

Thinking about this more and looking at other frameworks. It is super nice to have Cypress "automatically" transpile things for you, because it has to work in both the node and browser environment. The problem is that customization is much harder. We have to expose to you the ability to modify the options. This worked well for the file:preprocessor plugin event - because the browser context is always executed later in the testing lifecycle. However, trying to do the same thing with node is not possible because there has to be some kind of entry point.

If we think about this outside of just Typescript we have the same issue. Imagine someone wanting to write their node plugin files using ES2018 - they'd have the same problem as they do whenever they're starting a vanilla node project for the first time. In both cases, you'd have to use babel to automatically transpile the .js or .mjs files on the fly. The difference is that it would be totally controllable by the user (we should just write some docs outlining this behavior). If you think about Typescript in this way you could argue that we should not add built in native support for it so that the user retains total control of both the node and browser context. On the other hand we could simply avoid adding lots of configuration options and instead just allow the user to turn off built in typescript support and then provide docs on how to manually wire things up in the case that you need to customize things beyond what we support.

@brian-mann brian-mann added the stage: proposal 💡 No work has been done of this issue label Jun 3, 2018
@brian-mann
Copy link
Member Author

@amirrustam
Copy link
Contributor

amirrustam commented Jun 4, 2018

With regards to TypeScript support, here are current ts-node register options I'm using within the API. Documenting here for discussion.

require('ts-node').register({
  pretty: true,  // Use pretty diagnostic formatter
  typeCheck: false, // Turn off type checking
  transpileOnly: true,   // Use TypeScript's faster `transpileModule`
  ignoreDiagnostics: true,  // Ignore TypeScript warnings by diagnostic code
})

Full ts-node register options:

// Register options.
  pretty?: boolean
  typeCheck?: boolean
  transpileOnly?: boolean
  cache?: boolean
  cacheDirectory?: string
  compiler?: string
  ignore?: string | string[]
  project?: string
  skipIgnore?: boolean
  skipProject?: boolean
  ignoreDiagnostics?: string | string[]
  compilerOptions?: string

@jennifer-shehane
Copy link
Member

I don't know as much about the implementation details of this, but I would like automatic TypeScript support, without having to set up my own preprocessor api if I have a .ts file extension in my test directories. Would love to hear @NicholasBoll opinion.

@brian-mann
Copy link
Member Author

@jennifer-shehane sure, that is the goal, but if you read my proposal you'll see that we need to give you the ability to configure ts-node to achieve this. If we read in your .ts files without going through a hook or index.js that sets these options, then there's no way to configure the way ts-node works.

We could support these options via cypress.json but then we're back to the same ol' configuration for everything problem vs just letting people write code to do the stuff they want to do.

Just read the proposal and you'll see what the challenges are. When those questions are answered and we come up with a reasonable implementation then we can do it. Saying we want to support this is like saying we want to support all browsers. Naturally we do, but that's completely glossing over the implementation details that need to be worked out.

@Toxicable
Copy link
Contributor

Whats the need for processing the file in memory, why not write them out to some temp directory?

@brian-mann
Copy link
Member Author

We do write them out to a temp directory

@Toxicable
Copy link
Contributor

Gotcha, is there a reason why you cant use typescript directly then? it'll reduce a dependancy and give you more options for config.

@basarat
Copy link

basarat commented Jul 6, 2018

I would love to remove the need for messing with plugins/index.js along with needing to install webpack @cypress/webpack-preprocessor typescript ts-loader

Current process https://github.com/basarat/typescript-book/blob/master/docs/testing/cypress.md

Ideally cypress ships with ts-node and all we (the users) need to do is npm install cypress typescript, write a tsconfig.json and start cracking with .spec.ts files 🌹

@SeriousM
Copy link

SeriousM commented Jul 6, 2018

@basarat that's a great guide, thank you! I would love to have a shorter typescript way too.
Currently we're using typescript for the intellisense and omit typescript features like type annotations so that the code looks like es5/es6. Not optimal but works for simple use cases.

@agborkowski
Copy link

agborkowski commented Jul 26, 2018

First of all, nice project, i just R&D cy and its looks promising, anyway to consider the topic ts integration

documentation is laked, when i used configs in eg. tests wont run, thank of all tsconfig form git repository work better. To works with chia/mocha assertions you need extend litle bit tsconfig

{
    "compilerOptions": {
        "target": "es5",
        "module": "commonjs",
        "skipLibCheck": true, // do not check types in node_modules folder
        "noImplicitAny": false,
        /* Raise error on expressions and declarations with an implied 'any' type. */
        "strictNullChecks": true,
        /* Enable strict null checks. */
        "noImplicitThis": true,
        /* Raise error on 'this' expressions with an implied 'any' type. */
        "alwaysStrict": true,
        "lib": ["es5", "dom"],
        "types": ["cypress"],
        "typeRoots": [
            "node_modules/@types"
        ],
        "paths": {
            "*": [
                "node_modules/*",
                "src/types/*"
            ]
        },
        "strict": true,
        "sourceMap": true,
        "experimentalDecorators": true
    },
    "include": [
        "node_modules/cypress",
        "cypress/*/*.ts"
    ],
    "compileOnSave": false
}

and add to each ts file eg.:

/// <reference types="cypress"/>
import { expect } from 'chai';

consider to buildin ts support in Pm2 - has solution for it but this solution has +/-, what is
pros 👍

  • fast entry point
  • independent
  • isolation
    cons 👎
  • conflicts problems in windows with global ts/ts-node
  • problems with diff version of dependency eg. pm2 uses max 6.0.5 and in develop u uses 7.0.0

@SeriousM
Copy link

Are there any updates on this issue?

@jennifer-shehane
Copy link
Member

@SeriousM No updates on this issue. It is still in a proposal stage.

@aczekajski
Copy link

This might be an obvious thing to say but looking directly into webpack codebase might be a good idea. Webpack does exactly what this proposal wants to achieve. You can write configs in typescript and it just uses ts-node underneath with zero additional configuration needed - you just need to install ts-node as a devDep in you project. If you want to use different tsconfig, you just run it like this: TS_NODE_PROJECT="tsconfig-ts-node.json" webpack.

@SeriousM
Copy link

SeriousM commented Jan 17, 2019 via email

@bahmutov
Copy link
Contributor

@aczekajski do you have a complete example, even a very simple one that shows what you are doing in action? Because for me, configuring Webpack like this https://webpack.js.org/guides/typescript/ plus adding https://github.com/cypress-io/cypress-webpack-preprocessor is not as simple, and trying to make it "built-in" seems a little bit premature

@jraoult
Copy link

jraoult commented Jan 18, 2019

@bahmutov I think @aczekajski is only talking about that https://webpack.js.org/configuration/configuration-languages/. Seems indeed to be only a subset of this issue's objective :)

@bahmutov
Copy link
Contributor

would this step by step instruction webpack + Cypress help? https://glebbahmutov.com/blog/use-typescript-with-cypress/

@SeriousM
Copy link

SeriousM commented Jan 19, 2019 via email

@jennifer-shehane jennifer-shehane added stage: proposal 💡 No work has been done of this issue and removed stage: proposal 💡 No work has been done of this issue labels Jan 25, 2019
@ngbrown
Copy link

ngbrown commented Mar 26, 2019

Now with @babel\preset-typescript, this can be accomplished by just modifying the default browserify preprocessor.

To do this manually:

install @cypress/browserify-preprocessor and @babel/preset-typescript

Modify the plugins/index.js file:

const browserify = require('@cypress/browserify-preprocessor');

module.exports = (on, config) => {
  const options = browserify.defaultOptions;
  options.browserifyOptions.extensions.push('.ts', '.tsx');
  const babelifyConfig = options.browserifyOptions.transform[1][1];
  babelifyConfig.presets.push(require.resolve('@babel/preset-typescript'));
  babelifyConfig.extensions = ['.js', '.jsx', '.ts', '.tsx'];

  on('file:preprocessor', browserify(options));
};

@bahmutov
Copy link
Contributor

bahmutov commented Mar 26, 2019 via email

@basarat
Copy link

basarat commented Mar 26, 2019

I would be happy built-in babel doing the ts->js transform.
I use the IDE / tsc --noEmit to tell me any type errors anyways 🌹

@sainthkh
Copy link
Contributor

sainthkh commented Dec 7, 2019

The goal of this proposal is these 2 in summary:

  1. Out-of-the-box TypeScript support.
  2. Allow customization for it. (Even with Babel)

But after reading cypress code, I realized that files under cypress folder are handled in different ways.

  1. integration, support: They're transpiled by browserify and sent to client.
  2. plugins: They're required.

So, the goal can be broken into 4 like this:

  1. Out-of-the-box TypeScript support for test files.
  2. Out-of-the-box TypeScript support for plugin files.
  3. Allow customization for test files.
  4. Allow customization for plugin files.

I've created PR(#5906) to fix these problems. Here are the notes and thoughts about the process.

1. Out-of-the-box TypeScript support for test files.

About implementation

There were 2 options to make this possible.

  1. Use tsify and bundled/user-installed typescript.
  2. Use @babel/preset-typescript.

I decided to go with babel. Although it doesn't support type check, modern editors check TypeScript types. And tsify is too slow for the job.

Why not webpack?

We need to bundle webpack and loaders. It increases bundle size too much. So, I didn't do that.

How it works

When there is a user-installed @babel/preset-typescript, use it. If not, use bundled one.

Options

I'm thinking about going with no config for this. It's hard to find a reason to prefer bundled @babel/preset-typescript to user-installed one.

2. Out-of-the-box TypeScript support for plugin files.

About implementation

ts-node has to be added to packages/server dependencies. I created 4 options for this and register options accordingly.

Options & How it works

Add typescript option in cypress.json. It can have one of these 4 values.

  • undefined or default: use user-installed typescript if exists, use bundled one if not.
  • project: use user-installed typescript if exists, throw error if not.
  • bundled: always use bundled typescript
  • none: don't use typescript.

3. Allow customization for test files.

I think it's solved by "file:preprocessor".

When you want something complicated than out-of-the-box TypeScript, it should be done with this event.

4. Allow customization for plugin files.

Option ideas

I think there are 3 things that users want to customize.

  1. tsconfig
  2. ts-node options
  3. Set up everything you like

We can support them like this:

tsconfig

Add tsconfig option in cypress.json. It should have 4 options:

  • undefined or default: use user-installed tsconfig if exists, use bundled one if not.
  • project: use user-installed tsconfig if exists, throw error if not.
  • bundled: always use bundled tsconfig
  • when typescript option is none and this value is defined, throw an error.

ts-node options

Add tsnode option in cypress.json. You can add options if it is supported in ts-node.

Note: when typescript option is none and it is defined, throw an error.

Set up everything you like

Add setupNodeFile option in cypress.json. Its default value is plugins/setup_node.js. It must be js file.

In this file, users can define whatever they want here. If they want to set up babel-node with a bunch of stage-0/1 features, this is the place to do it.

If this file exists, all typescript-related options will be ignored.

Example Code
// plugins/setup_node.js
module.exports = () => {
    require('@babel/node')
}
//.babelrc
{
    // presets and plugins...
}

This setup_node.js file will be executed before plugins/index.js is loaded.

setup_node.js exports a function for the future.

The Problem

These are not implemented in #5906.

It's because of the noImplicitAny option in tsconfig. When it is set to true, server will throw an error because in packages/server, noImplicitAny is false.

Because of this, transpileOnly must be always true. If not, server will fail.

I think we should revisit this issue after #2690 is done. Or close this and create new issue for these options and wait for users' opinions.

These options might be over-engineering.

@bahmutov
Copy link
Contributor

bahmutov commented Dec 7, 2019 via email

@brian-mann
Copy link
Member Author

I don't think we should ever bundle typescript, and always require the user to have it installed. Adding the tsc binary increases the size of Cypress by too much. I also don't like the idea of the version of typescript not being specified. It makes more sense to always require the user to supply typescript - since they likely already are in their project in the first place.

You are right that we need to handle both the plugin files and the spec files separately, and because of that - that's where all the complexity emerges. I think your ideas do satisfy all the conditions, but that overall its still far too complex and would require far too much documentation and edge case handling. I also really don't like the idea of introducing yet another setupNodeFile configuration option.

I don't think we need to actually introduce any new configuration options - we could automatically detect what the user is trying to do via the .ts file extension and attempt to do the right thing.

I also think that we could just use tsconfig.json by default, and avoid needing to support any ts-node options - or at the very least, if the user supplies their own environment variables, then those would "just work".

@sainthkh
Copy link
Contributor

sainthkh commented Dec 26, 2019

@brian-mann Sorry for the late reply. I was investigating to find the answer.

My second proposal is this:

Focus on transpiling typescript files. Let editors and CIs do type checking.

With this, we don't have to worry about tsconfig.json, ts-node options, etc. All we do is these 2:

  1. When typescript is installed in user project, use it.
  2. When typescript is not installed in user project but .ts files are used. Throw error like now.

With this, new dependency we need to add is ts-node (resolve and through2 are added by babel and browserify). If that's too much, then we can give up typescript for plugin and remove ts-node.

Please check #5906 for code. (The failed test is flaky test to be fixed in #6030)

NOTE: To remove typescript from server entirely, we need to remove dependency-tree and find/make alternative. (I opened an issue in #6051)

@cypress-bot cypress-bot bot added stage: needs review The PR code is done & tested, needs review and removed stage: proposal 💡 No work has been done of this issue labels Jan 9, 2020
@cypress-bot cypress-bot bot added stage: work in progress and removed stage: needs review The PR code is done & tested, needs review labels Feb 3, 2020
@cypress-bot cypress-bot bot added stage: needs review The PR code is done & tested, needs review stage: work in progress and removed stage: work in progress stage: needs review The PR code is done & tested, needs review labels Mar 24, 2020
@cypress-bot cypress-bot bot added stage: needs review The PR code is done & tested, needs review and removed stage: work in progress labels Mar 31, 2020
@cypress-bot cypress-bot bot added stage: work in progress stage: needs review The PR code is done & tested, needs review and removed stage: needs review The PR code is done & tested, needs review stage: work in progress labels Apr 10, 2020
@cypress-bot cypress-bot bot added stage: pending release and removed stage: needs review The PR code is done & tested, needs review labels Apr 13, 2020
@cypress-bot
Copy link
Contributor

cypress-bot bot commented Apr 13, 2020

The code for this is done in cypress-io/cypress#5906, but has yet to be released.
We'll update this issue and reference the changelog when it's released.

@cypress-bot
Copy link
Contributor

cypress-bot bot commented Apr 13, 2020

Released in 4.4.0.

This comment thread has been locked. If you are still experiencing this issue after upgrading to
Cypress v4.4.0, please open a new issue.

@cypress-bot cypress-bot bot locked as resolved and limited conversation to collaborators Apr 13, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.