Ts-jest: Experiment: Try out TypeScript Program in compiler

Created on 4 Jul 2019  路  10Comments  路  Source: kulshekhar/ts-jest

Issue :

Hi guys! Is there a way to get access to the TS Program/TypeChecker instance from inside of a custom AST transformer?
There was a similar question #303 At that time it wasn't possible but now ts-jest uses TS language service I guess it is possible? Would passing the languageService to ConfigSet introduce some issues?

Enhancement Help Wanted

Most helpful comment

@mtebenev , @wtho Program is available now for transformers. You can test against master

All 10 comments

Hey!

I looked a bit into ts-jest recently to look for the TS Program (and the TypeChecker), but it seems to me that ts-jest does avoid to use it. I found this comment:

[...] If we don't return undefined it results in undefined === "undefined" and run createProgram again (which is very slow).

ts-jest exclusively uses transpileModule to transpile all tests independently, see https://github.com/kulshekhar/ts-jest/blob/master/src/compiler.ts#L97-L102.
Here ts is the actual resolved typescript module, but createProgram is never called.

Maybe we could learn e. g. from Angular/webpack, how it compiles incrementally using the TS language service with TS Program and TypeChecker (this is the place where Angular's webpack plugin creates the program etc). Obvoiusly tansformers without TypeChecker functionality are way less powerful and restricted. The language service itself gets created in createCompiler (compiler.ts) when isolatedModules is not set to true.

As an ugly workaround you could create your own program instance for the transformer using configSet.compilerModule.createProgram(configSet.typescript.fileNames, configSet.typescript.options, undefined) and then call program.getTypeChecker to access the TypeChecker.

I'm interested in your findings as well, cheers!

As an ugly workaround you could create your own program instance for the transformer using configSet.compilerModule.createProgram(configSet.typescript.fileNames, configSet.typescript.options, undefined) and then call program.getTypeChecker to access the TypeChecker.

@wtho I'm already doing this ugly workaround in my PoC project

https://github.com/mtebenev/typefixture/blob/5ccf238858b9f07e7d314ceb14a66edc30438f44/packages/core/src/kernel/instrumentation/fixture-reference-finder.ts#L23-L28

And I've found it extremely slow (adding roughle 1.5seconds per file). So if such PR would be accepted I can prepare modifications providing the language service for transformers.

I am not a ts-jest-member, just stumbled upon the same issue around the same time.

But I personally would be interested in performance results of an implementation with a single Program instance for all compilations done in ts-jest. Maybe calling transpileModule is actually slower when called for each file individually, e. g. if a big dependency tree has to be loaded every single time than doing it just once using the TS Program, and the comment in the code is a wrong assumption for big projects.

I am not in the position to suggest a PR, but I think it is valuable for ts-jest to learn about the performance implications of this implementation.

According to @huafu , he said once about transformer should be only initialized once for all tests. Does it mean about Program instance ?

No, it is not really related to the transformer lifecycle.

Basically, there are two ways to compile TS code to JS calling the tsc compiler programmatically:

  • transpileModule, which is a simpler, more naive approach of transpiling a simple module (usually a single file), it cannot verify import statements in depth
  • createProgram and program.emit creates a compiler host that gathers lots of context and diagnostics during the compilation, if program is used and supplied to transformers, they can access the TypeChecker, which knows about references of other modules, e. g. which class is referenced by a class reference. This additional context information can be super useful.

huafu once stated (see above) that createProgram is slow, and I totally agree it is slow when using it in unit tests for tiny applications. But in a big actual application, where the compiler host and program is created only once for all tests, it can be very helpful.

See https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API for further information.

Not dive into createProgram yet but creating instance once for all tests already sounds logical. I鈥檇 go for it.

How do we approach that solution ?

This might be too costly, as huafu stated before, but we should give it a shot.
I also have not worked much with the createProgram of tsc.
Probably it is best to create the program instance on the top level of createCompiler inside compiler.ts, or maybe even a language server instance, although I am also not too sure about the implications of it.

There is not much literature about the TS compiler API. It is probably best to have a look at how other compilers (e. g. Angular-cli, Vue-cli, webpack-ts-loader, webpack-awesome-typescript-loader) do this and learn from them. I looked a bit into Angular Compilation, but it is quite complicated, it might be better to have a look at the others (and check if they use createProgram).

Thanks for the info 馃檹, I鈥檓 curious how the examples do with that pattern

Updated: I have an idea for this experiment.

  • Before processing is invoked, we can create Program which will compile all ts files based on fileNames from ParsedCommandLine object.
  • In the emit method in Program, we can pass in transformers. This allows custom transformers specifying in ts-jest config.
  • update cache with emit result from Program
  • When 1st time processing is invoked, simply get emit result from Program.
  • When test is run again, jest will simply get result from cache.
  • When isolatedModules: true, use transpileModuleto compile.

I'd say the main changes will be:

  • Use Program instead of language service to compile when isolatedModules: false.
  • Compile before processing is invoked (this might be tricky).
  • Everything about cache, isolatedModules: true etc remains the same.
    Not sure how it will affect to the performance though.

What do you think about the approach @wtho @mtebenev ?
cc @kulshekhar

@mtebenev , @wtho Program is available now for transformers. You can test against master

Yeah! Thanks so much!

Was this page helpful?
0 / 5 - 0 ratings