<commit_before>import ts = require('typescript')
import { TsFileMap } from './ts-file-map'

export interface Result<T> {
  result: T | null
  errors: string[]
}

export class LanguageService {
  private files = new TsFileMap()
  private tsService: ts.LanguageService

  constructor (rootFileNames: string[], private options: ts.CompilerOptions) {
    rootFileNames.forEach(file => {
      this.files.updateFile(file)
    })

    const serviceHost = this.makeServiceHost(options)
    this.tsService = ts.createLanguageService(serviceHost, ts.createDocumentRegistry())
  }

  updateFile (fileName: string): void {
    this.files.updateFile(fileName)
  }

  getDts (fileName: string): Result<string> {
    fileName = normalize(fileName)

    // Unsupported files or not found
    if (!this.files.canEmit(fileName)) {
      return {
        result: null,
        errors: []
      }
    }

    const output = this.tsService.getEmitOutput(fileName, true)

    if (!output.emitSkipped) {
      const result = output.outputFiles
        .filter(file => /\.d\.ts$/.test(file.name))[0].text

      return {
        result,
        errors: []
      }
    }

    return {
      result: null,
      errors: this.collectErrorMessages(fileName)
    }
  }

  private makeServiceHost (options: ts.CompilerOptions): ts.LanguageServiceHost {
    return {
      getScriptFileNames: () => this.files.fileNames,
      getScriptVersion: fileName => this.files.getVersion(fileName),
      getScriptSnapshot: fileName => {
        const src = this.files.getSrc(fileName)
        return src && ts.ScriptSnapshot.fromString(src)
      },
      getCurrentDirectory: () => process.cwd(),
      getCompilationSettings: () => options,
      getDefaultLibFileName: options => ts.getDefaultLibFilePath(options)
    } as ts.LanguageServiceHost
  }

  private collectErrorMessages (fileName: string): string[] {
    const allDiagnostics = this.tsService.getCompilerOptionsDiagnostics()
      .concat(this.tsService.getSyntacticDiagnostics(fileName))
      .concat(this.tsService.getSemanticDiagnostics(fileName))

    return allDiagnostics.map(diagnostic => {
      const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')

      if (diagnostic.file) {
        const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start)
        return `[${line + 1},${character + 1}] ${message}`
      }
      return message
    })
  }
}

// .ts suffix is needed since the compiler skips compile
// if the file name seems to be not supported types
function normalize (fileName: string): string {
  if (/\.vue$/.test(fileName)) {
    return fileName + '.ts'
  }
  return fileName
}
<commit_msg>fix: avoid errors when occurred if diagnostic position is unknown<commit_after>import ts = require('typescript')
import { TsFileMap } from './ts-file-map'

export interface Result<T> {
  result: T | null
  errors: string[]
}

export class LanguageService {
  private files = new TsFileMap()
  private tsService: ts.LanguageService

  constructor (rootFileNames: string[], private options: ts.CompilerOptions) {
    rootFileNames.forEach(file => {
      this.files.updateFile(file)
    })

    const serviceHost = this.makeServiceHost(options)
    this.tsService = ts.createLanguageService(serviceHost, ts.createDocumentRegistry())
  }

  updateFile (fileName: string): void {
    this.files.updateFile(fileName)
  }

  getDts (fileName: string): Result<string> {
    fileName = normalize(fileName)

    // Unsupported files or not found
    if (!this.files.canEmit(fileName)) {
      return {
        result: null,
        errors: []
      }
    }

    const output = this.tsService.getEmitOutput(fileName, true)

    if (!output.emitSkipped) {
      const result = output.outputFiles
        .filter(file => /\.d\.ts$/.test(file.name))[0].text

      return {
        result,
        errors: []
      }
    }

    return {
      result: null,
      errors: this.collectErrorMessages(fileName)
    }
  }

  private makeServiceHost (options: ts.CompilerOptions): ts.LanguageServiceHost {
    return {
      getScriptFileNames: () => this.files.fileNames,
      getScriptVersion: fileName => this.files.getVersion(fileName),
      getScriptSnapshot: fileName => {
        const src = this.files.getSrc(fileName)
        return src && ts.ScriptSnapshot.fromString(src)
      },
      getCurrentDirectory: () => process.cwd(),
      getCompilationSettings: () => options,
      getDefaultLibFileName: options => ts.getDefaultLibFilePath(options)
    } as ts.LanguageServiceHost
  }

  private collectErrorMessages (fileName: string): string[] {
    const allDiagnostics = this.tsService.getCompilerOptionsDiagnostics()
      .concat(this.tsService.getSyntacticDiagnostics(fileName))
      .concat(this.tsService.getSemanticDiagnostics(fileName))

    return allDiagnostics.map(diagnostic => {
      const message = ts.flattenDiagnosticMessageText(diagnostic.messageText, '\n')

      if (diagnostic.file && diagnostic.start) {
        const { line, character } = diagnostic.file.getLineAndCharacterOfPosition(diagnostic.start)
        return `[${line + 1},${character + 1}] ${message}`
      }
      return message
    })
  }
}

// .ts suffix is needed since the compiler skips compile
// if the file name seems to be not supported types
function normalize (fileName: string): string {
  if (/\.vue$/.test(fileName)) {
    return fileName + '.ts'
  }
  return fileName
}

{'fraction_non_alphanumeric': 0.11680703039179788, 'fraction_numerical': 0.0010984987184181618, 'mean_word_length': 3.192632386799693, 'pattern_counts': {'":': 0, '<': 7, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7029918', 'n_tokens_mistral': 1559, 'n_tokens_neox': 1580, 'n_words': 346}