How does gulp-typescript incremental compilation work?
Asked Answered
C

2

12

gulp-typescript's README says it supports incremental compilation but doesn't explain what is meant by that term in this context.

There's an issue discussing how gulp-typescript incremental compilation is slower than tsc, but it doesn't explain why, or what the difference is.

What is gulp-typescript doing when it does "incremental compilation"?

What I tried

I'm looking at gulp-typescript's source code and it looks like gulp-typescript isn't doing any incremental compilation at all. When a Project is created, it closes over an instance of ProjectInfo, which has an input member which is a FileCache. A FileCache is pretty much a mapping from file names to source strings. There isn't much other state that is maintained in a gulp-typescript Project.

In order to do actual incremental compilation (reusing products of the compilation process in subsequent builds) using the TS compiler API, I would expect to see one of the following:

But I don't see either of those in the source.

Carp answered 15/2, 2019 at 20:25 Comment(0)
T
7

Gulp-typescript has two different ways to compile your files. The default method compiles the whole project and does type checking. This is implemented in ProjectCompiler in lib/compiler.ts. The other compiles each file separate, and is activated when you set isolatedModules: true. That method is implemented in FileCompiler in lib/compiler.ts.

The FileCompiler only needs to compile the files which are changed. Unchanged files are cached, like Achmedzhanov described in his answer.

Most users however use the ProjectCompiler, as type checking is probably the reason that they are using TypeScript. I think that your question regards this ProjectCompiler. Incremental compilation is handled by the call to ts.createProgram, which the TypeScript API exports. By passing the old program, the TypeScript API would reuse some of the information of the previous compilation. You can find the source code here:

https://github.com/ivogabe/gulp-typescript/blob/ea22fb7fe4295979e32a9d07b007e3f7473be8b5/lib/compiler.ts#L80

That used to be enough to get incremental compilation, but that has changed in newer versions of TypeScript. We will need to switch to a new API, probably using one of the APIs you mentioned, but I'm not familiar with those.

In your question you mentioned the FileCache. This is used to store all files that are passed in the input stream. The gulp API namely gives all files in a stream, whereas the TypeScript API is synchronous. We thus need to wait until we have all input files. Furthermore, we use the FileCache in the FileCompiler to detect whether a file has changed and thus whether we need to recompile it.

Teacake answered 25/2, 2019 at 10:36 Comment(2)
You're right that my question is about ProjectCompiler Does the ProjectCompiler let TypeScript know when a file has changed? What part of the emit and/or type checking is incremental? Does the incrmentality consist in gulp-typescript caching the FIle and ts.SourceFile objects? Or is it doing something else as well?Carp
In lib/input.ts we indeed cache the ts.SourceFile objects of the previous compilation. If a file is not changed, it is not parsed. I forgot to mention that in my answer. Furthermore, the TypeScript compiler will reuse some information stored with the ts.SourceFile, if I'm not mistaken it will cache the binding information there. I don't know how the TypeScript compiler handles the emit and type checking. In gulp-typescript we do not do anything incremental for that.Teacake
E
1

gulp-typescript caches compiled js files, when some file is changed then it compiles one https://github.com/ivogabe/gulp-typescript/blob/master/lib/compiler.ts#L282

if (this.project.input.getFileChange(file.fileNameOriginal).state === FileChangeState.Equal) {
    // Not changed, re-use old file.

    const old = this.previousOutput[file.fileNameNormalized];
    this.write(file, old.fileName, old.diagnostics, old.content, old.sourceMap);

    return;
}

const output: ts.TranspileOutput = this.project.typescript.transpileModule(file.content, {
    compilerOptions: this.project.options,
    fileName: file.fileNameOriginal,
    reportDiagnostics: true,
    transformers: this.finalTransformers ? this.finalTransformers() : undefined,
});

Even one file compilation causes analysis of imported dependecies so it couldn't be faster then tsc --watch

Elviraelvis answered 22/2, 2019 at 19:43 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.