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

Compilation very slow with TS v3.4 #148

Closed
Kerumen opened this issue Apr 9, 2019 · 28 comments
Closed

Compilation very slow with TS v3.4 #148

Kerumen opened this issue Apr 9, 2019 · 28 comments
Labels
kind: optimization Performance, space, size, etc improvement priority: blocked Progress on this is blocked problem: stale Issue has not been responded to in some time scope: upstream Issue in upstream dependency solution: tsc behavior This is tsc's behavior as well, so this is not a bug with this plugin topic: TS version Related to a change in a TS version

Comments

@Kerumen
Copy link

Kerumen commented Apr 9, 2019

What happens and why it is wrong

Once I upgraded TS to v3.4.2, the compilation time is very slow. With TS v3.3 my project built in ~20s and with TS v3.4 it takes ~5mn.

I tried with clean: true, I tried to add typescript: require('typescript'), I tried to upgrade rollup and all the related plugins.. Each time, same result the compilation takes a lot of time.

Note: when I run tsc -p tsconfig.prod.json directly, it takes less than 10 seconds to output the dist.

Environment

Versions

  • typescript: v3.4.2
  • rollup: v1.6.0
  • rollup-plugin-typescript2: v0.19.2

rollup.config.js

  {
    input: 'src/index.ts',
    output: [
      { file: pkg.main, format: 'cjs' },
      { file: pkg.module, format: 'es' },
    ],
    plugins: [
      external({ includeDependencies: true }),
      resolve(),
      typescript({
        tsconfig: './tsconfig.prod.json',
        rollupCommonJSResolveHack: true,
      }),
      commonjs(),
      filesize(),
    ],
  }

tsconfig.json

tsconfig.json
{
  "compilerOptions": {
    "module": "esnext",
    "target": "es5",
    "jsx": "react",
    "esModuleInterop": true,
    "lib": ["dom", "es2017"],
    "moduleResolution": "node",
    "rootDir": "src",
    "forceConsistentCasingInFileNames": true,
    "noImplicitReturns": true,
    "noImplicitThis": true,
    "noImplicitAny": true,
    "strictNullChecks": true,
    "suppressImplicitAnyIndexErrors": true,
    "noUnusedLocals": false,
    "downlevelIteration": true
  },
  "include": ["src/**/*", "types/**/*"]
}
tsconfig.prod.json
{
  "extends": "./tsconfig.json",
  "compilerOptions": {
    "outDir": "./dist",
    "noUnusedLocals": true,
    "sourceMap": true,
    "declaration": true
  },
  "exclude": ["src/doc", "**/stories.tsx", "**/test.tsx", "**/fixture.ts"]
}

package.json

    "rollup": "^1.6.0",
    "rollup-plugin-commonjs": "^9.2.1",
    "rollup-plugin-filesize": "^6.0.1",
    "rollup-plugin-node-resolve": "^4.0.1",
    "rollup-plugin-peer-deps-external": "^2.2.0",
    "rollup-plugin-typescript2": "^0.19.2",
    "typescript": "^3.4.2"

plugin output with verbosity 3

Output
rpt2: typescript version: 3.4.2
rpt2: tslib version: 1.9.3
rpt2: rollup-plugin-typescript2 version: 0.19.2
rpt2: plugin options:
{
    "tsconfig": "./tsconfig.prod.json",
    "rollupCommonJSResolveHack": true,
    "clean": true,
    "verbosity": 3,
    "typescript": "version 3.4.2",
    "check": true,
    "cacheRoot": "/Users/yannpringault/git/PayFit/components/.rpt2_cache",
    "include": [
        "*.ts+(|x)",
        "**/*.ts+(|x)"
    ],
    "exclude": [
        "*.d.ts",
        "**/*.d.ts"
    ],
    "abortOnError": true,
    "useTsconfigDeclarationDir": false,
    "tsconfigOverride": {},
    "transformers": [],
    "tsconfigDefaults": {},
    "objectHashIgnoreUnknownHack": false
}
rpt2: rollup config:
{
    "chunkGroupingSize": 5000,
    "experimentalCacheExpiry": 10,
    "inlineDynamicImports": false,
    "input": "src/index.ts",
    "perf": false,
    "plugins": [
        {
            "name": "peer-deps-external"
        },
        {
            "name": "node-resolve"
        },
        {
            "name": "rpt2"
        },
        {
            "name": "commonjs"
        },
        {
            "name": "filesize"
        }
    ]
}
rpt2: built-in options overrides: {
    "noEmitHelpers": false,
    "importHelpers": true,
    "noResolve": false,
    "noEmit": false,
    "inlineSourceMap": false,
    "outDir": "/Users/yannpringault/git/PayFit/components/.rpt2_cache/placeholder",
    "moduleResolution": 2,
    "allowNonTsExtensions": true,
    "declarationDir": "/Users/yannpringault/git/PayFit/components"
}
rpt2: parsed tsconfig: {
    "options": {
        "module": 6,
        "target": 1,
        "jsx": 2,
        "esModuleInterop": true,
        "lib": [
            "lib.dom.d.ts",
            "lib.es2017.d.ts"
        ],
        "moduleResolution": 2,
        "rootDir": "/Users/yannpringault/git/PayFit/components/src",
        "forceConsistentCasingInFileNames": true,
        "noImplicitReturns": true,
        "noImplicitThis": true,
        "noImplicitAny": true,
        "strictNullChecks": true,
        "suppressImplicitAnyIndexErrors": true,
        "noUnusedLocals": true,
        "downlevelIteration": true,
        "outDir": "/Users/yannpringault/git/PayFit/components/.rpt2_cache/placeholder",
        "sourceMap": true,
        "declaration": true,
        "configFilePath": "/Users/yannpringault/git/PayFit/components/./tsconfig.prod.json",
        "noEmitHelpers": false,
        "importHelpers": true,
        "noResolve": false,
        "noEmit": false,
        "inlineSourceMap": false,
        "allowNonTsExtensions": true,
        "declarationDir": "/Users/yannpringault/git/PayFit/components"
    },
    "fileNames": [
        "/Users/yannpringault/git/PayFit/components/src/index.ts",
        "/Users/yannpringault/git/PayFit/components/src/components/index.ts",
        "/Users/yannpringault/git/PayFit/components/src/components/Alert/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Alert/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Avatar/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Avatar/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/AvatarGroup/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/AvatarGroup/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Badge/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Badge/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Button/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Button/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Checkbox/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Checkbox/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/CheckboxGroup/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/CheckboxGroup/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/DatePicker/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/DatePicker/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/DatePicker/Popin/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Dropdown/MenuContext.ts",
        "/Users/yannpringault/git/PayFit/components/src/components/Dropdown/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Dropdown/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Dropdown/DropdownItem/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Dropdown/DropdownItem/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Field/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Field/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Icon/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Icon/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Input/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Input/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Layout/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Legend/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Legend/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Loader/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Loader/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/ProgressBar/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/ProgressBar/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/ProgressCircle/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/ProgressCircle/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/RadioGroup/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/RadioGroup/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Select/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Select/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Steps/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Steps/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/components/Body.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/components/Cell.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/components/Footer.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/components/Header.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/components/HeaderCell.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/components/HeaderRow.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/components/Limit.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Table/components/Row.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/TableGroup/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/TableGroup/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Tag/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Tag/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Text/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Toggle/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Toggle/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Tooltip/index.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/Tooltip/style.tsx",
        "/Users/yannpringault/git/PayFit/components/src/components/_helpers/colors.test.ts",
        "/Users/yannpringault/git/PayFit/components/src/components/_helpers/colors.ts",
        "/Users/yannpringault/git/PayFit/components/src/components/_helpers/react.ts",
        "/Users/yannpringault/git/PayFit/components/src/components/_helpers/string.test.ts",
        "/Users/yannpringault/git/PayFit/components/src/components/_helpers/string.ts",
        "/Users/yannpringault/git/PayFit/components/src/primitives/_helpers.tsx",
        "/Users/yannpringault/git/PayFit/components/src/primitives/box.tsx",
        "/Users/yannpringault/git/PayFit/components/src/primitives/text.tsx",
        "/Users/yannpringault/git/PayFit/components/src/utils/index.ts",
        "/Users/yannpringault/git/PayFit/components/src/utils/colors/index.ts",
        "/Users/yannpringault/git/PayFit/components/src/utils/shadows/index.ts",
        "/Users/yannpringault/git/PayFit/components/src/utils/shadows/test.ts",
        "/Users/yannpringault/git/PayFit/components/src/utils/zIndex/index.ts",
        "/Users/yannpringault/git/PayFit/components/types/index.d.ts",
        "/Users/yannpringault/git/PayFit/components/types/modules.d.ts"
    ],
    "typeAcquisition": {
        "enable": false,
        "include": [],
        "exclude": []
    },
    "raw": {
        "extends": "./tsconfig.json",
        "compilerOptions": {
            "outDir": "./dist",
            "noUnusedLocals": true,
            "sourceMap": true,
            "declaration": true
        },
        "exclude": [
            "src/doc",
            "**/stories.tsx",
            "**/test.tsx",
            "**/fixture.ts"
        ],
        "compileOnSave": false,
        "include": [
            "src/**/*",
            "types/**/*"
        ]
    },
    "errors": [],
    "wildcardDirectories": {
        "/users/yannpringault/git/payfit/components/src": 1,
        "/users/yannpringault/git/payfit/components/types": 1
    },
    "compileOnSave": false,
    "configFileSpecs": {
        "includeSpecs": [
            "src/**/*",
            "types/**/*"
        ],
        "excludeSpecs": [
            "src/doc",
            "**/stories.tsx",
            "**/test.tsx",
            "**/fixture.ts"
        ],
        "validatedIncludeSpecs": [
            "src/**/*",
            "types/**/*"
        ],
        "validatedExcludeSpecs": [
            "src/doc",
            "**/stories.tsx",
            "**/test.tsx",
            "**/fixture.ts"
        ],
        "wildcardDirectories": {
            "/users/yannpringault/git/payfit/components/src": 1,
            "/users/yannpringault/git/payfit/components/types": 1
        }
    }
}
rpt2: included:
'[
    "*.ts+(|x)",
    "**/*.ts+(|x)"
]'
rpt2: excluded:
'[
    "*.d.ts",
    "**/*.d.ts"
]'
@deini
Copy link

deini commented Apr 11, 2019

I'm having the same issue. Went from 24s to 345s when bumping TS from 3.3.4000 to 3.4.3

@ezolenko
Copy link
Owner

ezolenko commented Apr 11, 2019

I tried to reproduce and at first I saw a slowdown, but using clean: true and going back and forth between typescript versions several times, I don't see the difference on clean build now...

Could you set clean: true, update rtp2 to latest (0.20.1 or master) and time a build several times in a row on ts 3.3 and ts 3.4?

@Kerumen
Copy link
Author

Kerumen commented Apr 15, 2019

@ezolenko Sorry for the late reply!

I put clean: true and upgraded rtp2 to 0.20.1. Here are the results:

TS 3.3.3333

build -> 18s
tsc   -> 14.39s

TS 3.3.4000

build -> 17.3s
tsc   -> 14.39s

TS 3.4.1

build -> 5m 25.1s
tsc   -> 21.26s

TS 3.4.3

build -> 5m 52.7s
tsc   -> 21.57s

Additionally there is this issue on the TS repo which seems related but I have @types/styled-components pinned to v4.1.4 so it doesn't cover my case.

Let me know if you need more infos.


For fun I also tried the latest 3.5 build:

TS 3.5.0-dev.20190413

build -> 1m 23.5s
tsc   -> 18.84s

@ezolenko
Copy link
Owner

ezolenko commented Apr 23, 2019

That issue you found (microsoft/TypeScript#30663) and the fact that 3.5-dev became 5 times faster for you makes me think they did something with LanguageService API. This is the API rpt2 uses as part of compilation and what typecheckers in IDEs are using (that's why WebStorm times out etc).

Your project might be using affected type structures elsewhere, that's why pinning styled-components doesn't help. And that's why I don't see a big slowdown when building rpt2 itself for example.

I guess we'll wait for final 3.5 release.

@ezolenko ezolenko added the priority: blocked Progress on this is blocked label Apr 23, 2019
@ezolenko
Copy link
Owner

btw, try building with check: false option

@tomasro27
Copy link

Has anyone confirmed if this is this fixed with 3.5 release?

@bradc1105
Copy link

@tomasro27 Not for me, nor with 6 (3.6.0-dev.20190621). Seems < 3.3 is still the fastest.

@LiamMartens
Copy link

It looks like 3.5 is a little bit faster but it could still be better

@donifer
Copy link

donifer commented Jul 23, 2019

Is there any update on this? Seem like its still blocked by TS?

@ezolenko
Copy link
Owner

Yeah, I don't think anything can be done from this end. Anybody is welcome to try though :)

@fobdy
Copy link

fobdy commented Aug 15, 2019

+1
3.5.3 is also very slow for us

@ritwickdey
Copy link

ritwickdey commented Aug 28, 2019

check: false is working great.

But the problem is that it doesn't give any compilation error (if you use vscode, it'll prompt the errors 😄).

At least I can do check: false when I'm working on CSS (in JS). 👍

@ZainlessBrombie
Copy link

@ezolenko do you know what exact typescript api call causes the build to slow down? :)

@ezolenko
Copy link
Owner

@ZainlessBrombie no, everything I know is in this thread. Typescript issue that is linked is probably related, but not direct cause (because they say it should be fixed in 3.5+, and apparently it isn't).

If you have slow builds in your project, check if ts 3.3 is actually faster if possible, maybe the problem is not typescript, but something else.

@ZainlessBrombie
Copy link

ZainlessBrombie commented Mar 10, 2020

Alright thanks. If I get around to it this might be a wall I'm willing to bash my head against so to speak :)

Edit: This problem only occurs for files that import styled components as it seems
Also, Typescript documentation is terrible

@ZainlessBrombie
Copy link

ZainlessBrombie commented Mar 10, 2020

Profiling typescript reveals the source of the problem to be these extremely large unions that styled components has. The only place to fix this is typescript, unless there is a way to get typescript to cache module resolution (which it should).

@ezolenko
Copy link
Owner

I'm actually not using module resolution cache in nodeModuleNameResolver call here:

const result = tsModule.nodeModuleNameResolver(importee, importer, parsedConfig.options, tsModule.sys);

Not sure if that makes ts use default one or not use one at all.

@ZainlessBrombie
Copy link

ZainlessBrombie commented Mar 11, 2020

Yes I saw that TODO :)
Unfortunately debugging the module resolution cache when used shows that it caches rebass but not styled components for some reason. It passes an internal cache for those...

@Danielku15
Copy link

I am currently in the process of migrating my C# codebase of my library to TypeScript and I'm using Rollup for bundling. (I had a custom C# > Haxe > JavaScript toolchain before). Unfortunately I am suffering also quite a lot on the compilation speed of my new TypeScript codebase which makes development quite hard.

If possible I am happy to provide some profiling reports if it is possible to generate them.

Here some insights to my project:

Usually I'm using ttypescript to do to some AST transformation.
Lines of Code: ~50000

rollup -c rollup.config.ts: ~9-10sec
rollup -c rollup.config.ts -w (initial compile): ~9-10sec
rollup -c rollup.config.ts -w (adding 1 alert in 1 TS file): ~20sec
rollup -c rollup.config.ts -w (removing alert again): ~20sec

tsc --version: 3.8.3
tsc --project tsconfig.json: ~6-7sec
tsc --project tsconfig.json -w (initial compile): ~6-7sec
tsc --project tsconfig.json -w (adding 1 alert in 1 TS file): ~0.1-0.2sec
tsc --project tsconfig.json -w (removing alert again): ~0.1-0.2sec

ttsc --version: 3.8.3
ttsc --project tsconfig.json: ~7-8sec
ttsc --project tsconfig.json -w (initial compile): ~6-7sec
ttsc --project tsconfig.json -w (adding 1 alert in 1 TS file): ~0.1-0.2sec
ttsc --project tsconfig.json -w (removing alert again): ~0.1-0.2sec

Incremental builds are active in the tsconfig.

It seems to me quite unusual that an incremental/watch build would take twice as much than a full build.

Things seem to go crazy in my environment when ti comes to testing. I hooked up rollup with karma and this typescript plugin. As you might know: each test suite gets its own bundle, which means the numbers of above pile up. Memory grows to >2GB over time; even after minutes the test run does not actually start. This happens on each single npm run test.

Once it reaches the "Generating bundle for..." area on the test generation I see ridiculous high numbers:
File01.test.ts containing 1 simple test of a parser class takes 15secs
File02.test.ts containing 11 XML parsing tests needs 2.5minutes
File03.test.ts did not even complete after 5 minutes.

I have no clue where this issue might be caused and if it is really due to the typescript module of rollup but this seemed to be the most likely issue I'm related to.

As my repo is quite large and complicated I did not share more details on configs. If anybody can guide me how to collect them, I'm happy to provide logs/profiling results etc. I will try to setup a new simple independent repository reflecting my overall project situation, but I cannot promise that the issue will persist.

@ezolenko
Copy link
Owner

ezolenko commented Apr 6, 2020

@Danielku15 Your main problem is that you are migrating from C# and not from C++ :)

This plugin is not explicitly using typescript's own incremental builds (this didn't exist when it started).

You could try @rollup/plugin-typescript, they use a different approach I think and might give you different numbers.

Another option is to do typescript compilation out of rollup chain (as a prebuild step in npm or something) and feed resulting js to rollup instead. Watch build would be interesting, although I think you can start tsc and rollup in watch mode and if project is configured correctly it will just work.

@Danielku15
Copy link

Danielku15 commented Apr 6, 2020

Your main problem is that you are migrating from C# and not from C++

Off-Topic: I am not sure if I would like to migrate a C++ framework/library to TypeScript. Depending on the use of macros and templates (which C++ devs tend to love) things might go wild during migration 😅 Luckily my C# codebase was always tailored to a style that allows cross compilation to other languages so the result is quite acceptable from a code structure. I just need to figure out a bit better how to organize the modules to reduce some overhead. 😊

This plugin is not explicitly using typescript's own incremental builds (this didn't exist when it started).

This is interesting, is this plugin having some own custom "incremental" build strategy? This could explain why a full-clean build might be taking longer than an source change when using watch. The typescript compilation is more or less stable, it is unfortunate that this plugin cannot utilize the incremental updates yet, but I think the time spent in the type script compiler is stable. But the time spend in rollup seems to be varying. Only this explains to me that a clean build is faster than a code change with watch-mode.

You could try @rollup/plugin-typescript ...

Thanks for the hint I will give it a try to see if it improves.

Another option is to do typescript compilation out of rollup chain

I had such a setup at the very beginning but dropped it half way to have "watch" builds during development. I will give it again a shot if I can hook up a pipeline with the two tools "watching" independently. 👍

Update 1: I was able to setup my whole compile and test environment now with invoking tsc chained with rollup. I needed to make my own rollup plugin to resolve the tsconfig paths correctly but after that it was working fine.

Clean compilation: 9secs, Watch compilation: 0.5-1secs and tests via Karma + Rollup are also running (compilation there takes a bit longer as it generates a bundle for each test suite).

@vinayaksh42
Copy link

I tried to reproduce and at first I saw a slowdown, but using clean: true and going back and forth between typescript versions several times, I don't see the difference on clean build now...

Could you set clean: true, update rtp2 to latest (0.20.1 or master) and time a build several times in a row on ts 3.3 and ts 3.4?

Where do I have to add this? rollup.config or tsconfig?

@fobdy
Copy link

fobdy commented Aug 18, 2021

The bitter truth now is that you should use esbuild plugin instead of this one. And if you want a type checking you just need to run tsc -noEmit directly as an additional step.

@vinayaksh42

This comment was marked as off-topic.

@fobdy
Copy link

fobdy commented Aug 18, 2021

Yep, sorry, for producing d.ts you should run tsc directly in an extra step with the emitDeclarationOnly option enabled (in this case you don't need noEmit anymore since it will also check types).
Or you can use Vite where everything should work out of the box.

P.S. There is Vue 2 plugin for Vite.

@panoply
Copy link

panoply commented Nov 21, 2021

The bitter truth now is that you should use esbuild plugin instead of this one.

It's important to note that even though esbuild is dope you will sacrifice some rather essential TypeScript features. Like for one, const enums are not supported plus a couple of other caveats. Another rather important feature that is lacking if one would choose to adopt the esbuild plugin into their stack will be the inability to elegantly handle TypeScript paths{} (remap imports).

As per issue 70, rollup-plugin-esbuild requires you lean on @rollup/plugin-alias for handling remaps which is far from ideal. This becomes rather problematic in large projects that take advantage of import remaps because @rollup/plugin-alias is slow, verbose and requires you to explicitly define the entries and thus those speed gains made with esbuild are quickly taken away.

If you decide to choose the official @rollup/typescript plugin then be rest assured at some point you will run into issues and find yourself trying to debug something that will be directly related to that plugin. Given the rather incorrigible nature some official rollup plugin maintainers happen to retain - when (and trust me, it is a matter of when) you find yourself scouring previous issues of the official plugins repository in an attempt to fix whatever the problem you've encountered might be or even if you wish to submit a PR it will be met with red-tape and fatigue. Essentially, the juice is really not worth the squeeze on that one imo.

There is also tszip maintained by ctjlewis which might be a solution for some as it is using a tsc to rollup compile approach and can be customized depending on your time and needs.

The fact remains that ts2 is clearly the breadwinner in the nexus of Rollup and TS support. It is not the fastest solution but it is the most stable and well maintained (side note: thanks ezolenko). There are some things you can do to help negate that, like those mentioned in previous comments here and while I am not sure if applicable maybe assumeChangesOnlyAffectDirectDependencies option within tsconfig.json could help?

Wishing everyone good karma, heath and happiness!

@cookejames
Copy link

For anyone else coming to this issue after finding rollup builds were intolerably (25 minutes for a large project) our solution after some investigation was rather simple. Setting declaration: false in tsconfig.json resulted in builds going down to under a minute. We now generate typings separately using tsc which only takes about 10s.

@agilgur5 agilgur5 added scope: upstream Issue in upstream dependency topic: TS version Related to a change in a TS version labels May 8, 2022
@agilgur5 agilgur5 added solution: tsc behavior This is tsc's behavior as well, so this is not a bug with this plugin problem: stale Issue has not been responded to in some time kind: optimization Performance, space, size, etc improvement and removed help wanted labels Sep 5, 2022
@agilgur5
Copy link
Collaborator

agilgur5 commented Sep 5, 2022

upstream issue

Hi folks, just doing some housekeeping in the issues. I'm going to close this one out as it's gone quite stale (no comments in 9 months) and as far as I can tell, there really isn't anything we can do on this front since the slowdown is within TypeScript itself and not this plugin. rpt2 also does not specify a specific version of TS and is compatible with many versions (we only specify a minimum TS version).

This issue with TS v3.4 was also reproduced by other tools in TS ecosystem, e.g. s-panferov/awesome-typescript-loader#633 . So it's not specific to this plugin either (or even Rollup) in that sense.

rpt2 internal nuances

I've made a handful of optimizations here and there since I joined on to help maintain rpt2, but most of those have been cache or watch mode related. The CPU-bound nature of compilation and type-checking still remains and those are primarily just calls to the TS Compiler API, so there isn't much speed-up to be had there.
The rpt2 codebase is relatively small too, with the source at a bit over ~1k LoC, so very generically speaking, there isn't too much overhead in the codebase itself.

We use the LanguageService API that IDEs use, and if I had to guess, this is slower than tsc because it's a file-oriented API as opposed to a whole Program-oriented API. tsc is also faster as a single tool that encompasses the whole build, whereas rpt2 is just a single plugin in a Rollup build (and multiple plugins may perform parsing, as well as Rollup itself).

Rollup also processes the code graph on a file-by-file basis, so file-oriented APIs are somewhat necessary for us to use. If we offload type-checking to another thread, as #113 suggests, then it might be possible to operate a bit differently in that case.
But that has its own caveats, as if we don't error out on type-check errors, that can cause the output JS to be problematic and cause Rollup to error out confusingly instead of a nice type error.
Plus, the history of Webpack threading has shown that moving type-checking into a separate thread is not always a performance improvement and can sometimes negatively impact performance instead (it also uses more memory, since Node worker_threads are quite primitive with sharing memory, so they're almost like separate processes). This likely depends heavily on the size and structure of your project, as well as the complexity of your types. I'll do a deep dive with Webpack plugin issue references in that issue later, but that's the gist of it.

tuning

Overall, I don't think there's anything we can do to address this issue specifically. There may be other optimizations to be had in other issues, but this one itself is generic, caused by upstream TS itself, and doesn't really have a course of action we can take.
That being said, please feel free to continue recommending specific optimizations and the performance characteristics of different configurations, as those can be helpful / useful, especially to other users!

Off the top of my head, a few configurations that can change performance characteristics are:

rpt2 options:

  • check: false disables type-checking
  • clean: true disables rpt2's caching. this may slow down some projects and speed up others, it depends. I imagine turning caching off may speed up compilation of smaller projects, where the I/O of a filesystem cache may not be worthwhile, whereas the default caching is probably better for larger projects. This has not been thoroughly benchmarked though.
  • exclude can be used to optimize filtering of files
  • transformers in that less transformations = better perf
  • useTsconfigDeclarationDir may improve perf as it skips Rollup's emit APIs and instead writes directly to the FS. It may also improve perf if you have multiple Rollup outputs.

Rollup options:

  • sourcemap: false disables source maps
  • potentially others that I can't think of off the top of my head

tsconfig options:

  • emitDeclarationOnly: true is supported as of 0.33.0 / feat: support emitDeclarationOnly #366, if you want to use Babel, esbuild, swc etc as transpiler and rpt2 as type-checker + declaration emitter. As others have mentioned above, note the caveats of not being able to use const enum etc with this configuration
  • declaration: false + declarationMap: false disables declarations and declaration maps
  • some other diagnostic and type-checking strictness options may also change performance, but it's not clear how
  • See also the official TS Performance docs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
kind: optimization Performance, space, size, etc improvement priority: blocked Progress on this is blocked problem: stale Issue has not been responded to in some time scope: upstream Issue in upstream dependency solution: tsc behavior This is tsc's behavior as well, so this is not a bug with this plugin topic: TS version Related to a change in a TS version
Projects
None yet
Development

No branches or pull requests