Skip to content

TypeScript 2.9 Watch API change breaking watch support in ts-loader? #24625

Closed

Description

Hey everybody! (and in particular Sheetal Nandi (@sheetalkamat) and Andy (Andrewkraft) (@Andy-MS) !)

TypeScript Version: 2.9.1

Apologies for raising a bug. I suspect this probably isn't a bug. (Although I suppose it may be.) I'm raising this issue because it may be a bug; but either way I could really benefit from some guidance.

I'm one of the maintainers of ts-loader. I was just doing the relevant housekeeping to cut a new release. I thought I'd switch to building ts-loader with TypeScript 2.9. However, this presents the following compile error:

src/servicesHost.ts:190:9 - error TS2322: Type '{ rootFiles: string[]; options: CompilerOptions; useCaseSensitiveFileNames: () => boolean; getNew...' is not assignable to type 'WatchHost'.
  Types of property 'createProgram' are incompatible.
    Type '{ (newProgram: Program, host: BuilderProgramHost, oldProgram?: BuilderProgram | undefined, config...' is not assignable to type 'CreateProgram<BuilderProgram>'.
      Types of parameters 'newProgram' and 'rootNames' are incompatible.
        Type 'ReadonlyArray<string> | undefined' is not assignable to type 'Program'.
          Type 'undefined' is not assignable to type 'Program'.

190   const watchHost: WatchHost = {
            ~~~~~~~~~

The error message isn't as revealing as it might be but I think it's related to createAbstractBuilder somehow no longer aligning with createProgram.

The easiest way to see this in action is to take the ts-loader source code and amend this line from:

    createProgram: compiler.createAbstractBuilder as any // TODO: Chase up with TypeScript team about API change

to:

    createProgram: compiler.createAbstractBuilder

et voila! Compile error.

I've tried digging through the history of builder.ts and I've a hunch that this may be linked to the strictNullChecks switch that recently happened. Unfortunately I'm not getting enough information from the compiler to meaningful progress this. I'm drawing a blank.

Would you be able to give me some guidance please? My apologies if I'm missing something super obvious.

Activity

  1. changed the title [-]TypeScript 2.9 Watch API change breaking Watch support in ts-loader?[/-] [+]TypeScript 2.9 Watch API change breaking watch support in ts-loader?[/+] on Jun 2, 2018
  2. felixrabe commented on Jun 3, 2018

    @felixrabe

    I also have code that might break because of this ... bug / misunderstanding, that I based on the pull request for https://github.com/Microsoft/TypeScript-wiki/blob/master/Using-the-Compiler-API.md#writing-an-incremental-program-watcher:

    import chokidar from 'chokidar'
    import fs from 'fs-extra'
    import path from 'path'
    import { BrowserWindow } from 'electron'
    import ts from 'typescript'
    
    import pkgDir from 'pkg-dir'
    
    const ROOT = pkgDir.sync(__dirname)
    if (!ROOT) {
      throw new Error(`Could not find a valid project root.`)
    }
    
    const ROOT_BUILD_GUI = path.resolve(ROOT, 'build', 'gui')
    const ROOT_GUI = path.resolve(ROOT, 'gui')
    
    // based on: https://github.com/Microsoft/TypeScript-wiki/pull/169/
    const watchGuiTS = async (): Promise<void> => {
      let resolve: () => void
      const promise = new Promise<void>((res) => { resolve = res })
    
      const formatHost: ts.FormatDiagnosticsHost = {
        getCanonicalFileName: p => p,
        getCurrentDirectory: ts.sys.getCurrentDirectory,
        getNewLine: () => ts.sys.newLine
      }
    
      const formatDiagnostic = (diag: ts.Diagnostic) => {
        return ts.formatDiagnostic(diag, formatHost).trimRight()
      }
    
      // @ts-ignore
      const ignoreDiagnostic = (diag: ts.Diagnostic) => {
        // return [6031, 6032, 6042].indexOf(diag.code) !== -1
        return false
      }
    
      const reportDiagnostic = (diag: ts.Diagnostic) => {
        if (!ignoreDiagnostic(diag)) {
          console.error(formatDiagnostic(diag))
        }
      }
    
      let firstTS6042 = true
    
      const reportWatchStatusChanged = (diag: ts.Diagnostic) => {
        if (firstTS6042 && diag.code === 6042) {
          resolve()
          firstTS6042 = false
        }
        if (!ignoreDiagnostic(diag)) {
          console.info(formatDiagnostic(diag))
        }
      }
    
      const searchPath = ROOT_GUI
    
      const configPath = ts.findConfigFile(searchPath, ts.sys.fileExists, 'tsconfig.json')
      if (!configPath) {
        throw new Error(`Could not find a valid 'tsconfig.json'.`)
      }
    
      const host = ts.createWatchCompilerHost(configPath, {}, ts.sys,
        undefined,
        reportDiagnostic,
        reportWatchStatusChanged,
      )
    
      ts.createWatchProgram(host)
    
      return promise
    }
    
    // ...

    (I've used to use ts.createEmitAndSemanticDiagnosticsBuilderProgram instead of undefined in the call to createWatchCompilerHost and had to change that after upgrading to TS 2.9 (seems to be used by default anyway).)

    Calling watchGuiTS now results in these two TS-related messages, and as there is no TS6042 message anymore, the function does not get resolved:

    message TS6031: Starting compilation in watch mode...
    message TS6194: Found 0 errors. Watching for file changes.
    

    So this is where I'm stuck getting TS 2.9(.1) to work with my project ATM.

  3. felixrabe commented on Jun 3, 2018

    @felixrabe

    It might be that my issue (apart from the undefined thing that might be connected to this bug) is unrelated: I just saw that https://github.com/Microsoft/TypeScript/blob/master/src/compiler/diagnosticMessages.json is now missing TS6042 (it was present in the 2.8.3 version), and TS6194 with 0 errors might be the appropriate replacement. (Just guessing, not going on a code dig now.)

  4. felixrabe commented on Jun 3, 2018

    @felixrabe

    Ok, I ended up changing my code around reportWatchStatusChanged to the following:

      const formatDiagnostic = (diag: ts.Diagnostic) => {
        return ts.formatDiagnostic(diag, formatHost).trimRight()
      }
    
      const ignoreDiagnostic = (diag: ts.Diagnostic) => {
        return [6031, 6032].indexOf(diag.code) !== -1
      }
    
      const reportDiagnostic = (diag: ts.Diagnostic) => {
        if (!ignoreDiagnostic(diag)) {
          console.error(formatDiagnostic(diag))
        }
      }
    
      let firstCompilationDone = false
    
      const isCompilationDone = (diag: ts.Diagnostic) => {
        return diag.code === 6194 && diag.messageText === 'Found 0 errors. Watching for file changes.'
      }
    
      const reportWatchStatusChanged = (diag: ts.Diagnostic) => {
        if (!firstCompilationDone && isCompilationDone(diag)) {
          resolve()
          firstCompilationDone = true
        }
        if (!ignoreDiagnostic(diag)) {
          console.info(formatDiagnostic(diag))
        }
      }

    The part diag.code === 6194 && diag.messageText === 'Found 0 errors. Watching for file changes.' smells like "there is a better way to do this" but it works for now.

  5. sheetalkamat commented on Jun 4, 2018

    @sheetalkamat
    Member

    Felix Rabe (@felixrabe) you want to look for diagnostics code: 6193 and 6194 since those are the replacement for Watching for file changes message.

  6. added 2 commits that reference this issue on Jun 4, 2018
    e883051
    42a1164
  7. johnnyreilly commented on Jun 4, 2018

    @johnnyreilly
    Author

    Well done! 🌻

  8. mhegazy commented on Jun 4, 2018

    @mhegazy
    Contributor

    fix should be in typescript@next later tonight, and in typescript@2.9.2 in a week.

  9. locked as resolved and limited conversation to collaborators on Oct 21, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

BugA bug in TypeScriptDomain: APIRelates to the public API for TypeScriptFixedA PR has been merged for this issue

Type

No type

Projects

No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions