diff --git a/src/compiler/core.ts b/src/compiler/core.ts index 1382a941204..b028c1ab0c0 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -714,7 +714,7 @@ namespace ts { /** * List of supported extensions in order of file resolution precedence. */ - export const supportedExtensions = [".ts", ".d.ts", ".tsx"]; + export const supportedExtensions = [".ts", ".tsx", ".d.ts"]; const extensionsToRemove = [".d.ts", ".ts", ".js", ".tsx", ".jsx"]; export function removeFileExtension(path: string): string { diff --git a/src/compiler/program.ts b/src/compiler/program.ts index 4e13de58953..227372ce30d 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -152,10 +152,7 @@ namespace ts { const newLine = getNewLineCharacter(options); - let moduleResolutionHost: ModuleResolutionHost = { - fileExists: fileName => sys.fileExists(fileName), - } - + return { getSourceFile, getDefaultLibFileName: options => combinePaths(getDirectoryPath(normalizePath(sys.getExecutingFilePath())), getDefaultLibFileName(options)), @@ -164,7 +161,8 @@ namespace ts { useCaseSensitiveFileNames: () => sys.useCaseSensitiveFileNames, getCanonicalFileName, getNewLine: () => newLine, - getModuleResolutionHost: () => moduleResolutionHost + fileExists: fileName => sys.fileExists(fileName), + readFile: fileName => sys.readFile(fileName) }; } @@ -228,11 +226,10 @@ namespace ts { if (!options.noResolve) { resolveModuleNamesWorker = host.resolveModuleNames; if (!resolveModuleNamesWorker) { - Debug.assert(host.getModuleResolutionHost !== undefined); let defaultResolver = getDefaultModuleNameResolver(options); resolveModuleNamesWorker = (moduleNames, containingFile) => { return map(moduleNames, moduleName => { - let moduleResolution = defaultResolver(moduleName, containingFile, options, host.getModuleResolutionHost()); + let moduleResolution = defaultResolver(moduleName, containingFile, options, host); return moduleResolution.resolvedFileName; }); } @@ -248,7 +245,8 @@ namespace ts { if ((oldOptions.module !== options.module) || (oldOptions.noResolve !== options.noResolve) || (oldOptions.target !== options.target) || - (oldOptions.noLib !== options.noLib)) { + (oldOptions.noLib !== options.noLib) || + (oldOptions.jsx !== options.jsx)) { oldProgram = undefined; } } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e22e80ea796..1bac2917e8a 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2244,6 +2244,7 @@ namespace ts { export interface ModuleResolutionHost { fileExists(fileName: string): boolean; + readFile(fileName: string): string; } export interface ResolvedModule { @@ -2253,7 +2254,7 @@ namespace ts { export type ModuleNameResolver = (moduleName: string, containingFile: string, options: CompilerOptions, host: ModuleResolutionHost) => ResolvedModule; - export interface CompilerHost { + export interface CompilerHost extends ModuleResolutionHost { getSourceFile(fileName: string, languageVersion: ScriptTarget, onError?: (message: string) => void): SourceFile; getCancellationToken?(): CancellationToken; getDefaultLibFileName(options: CompilerOptions): string; @@ -2263,12 +2264,14 @@ namespace ts { useCaseSensitiveFileNames(): boolean; getNewLine(): string; - // Compiler host must implement one of these methods - // if getModuleResolutionHost is implemented then compiler will apply one of built-in ways to resolve module names - // and ModuleResolutionHost will be used to ask host specific questions - // if resolveModuleName is implemented - this will mean that host is completely in charge of module name resolution + /* + * CompilerHost must either implement resolveModuleNames (in case if it wants to be completely in charge of + * module name resolution) or provide implementation for methods from ModuleResolutionHost (in this case compiler + * will appply built-in module resolution logic and use members of ModuleResolutionHost to ask host specific questions). + * If resolveModuleNames is implemented then implementation for members from ModuleResolutionHost can be just + * 'throw new Error("NotImplemented")' + */ resolveModuleNames?(moduleNames: string[], containingFile: string): string[]; - getModuleResolutionHost?(): ModuleResolutionHost; } export interface TextSpan { diff --git a/src/harness/harness.ts b/src/harness/harness.ts index 645bcb46964..5d5d020f2b7 100644 --- a/src/harness/harness.ts +++ b/src/harness/harness.ts @@ -893,11 +893,7 @@ module Harness { newLineKind === ts.NewLineKind.CarriageReturnLineFeed ? carriageReturnLineFeed : newLineKind === ts.NewLineKind.LineFeed ? lineFeed : ts.sys.newLine; - - let moduleResolutionHost: ts.ModuleResolutionHost = { - fileExists: fileName => getSourceFile(fileName, ts.ScriptTarget.ES5) !== undefined, - } - + return { getCurrentDirectory, getSourceFile, @@ -906,7 +902,8 @@ module Harness { getCanonicalFileName, useCaseSensitiveFileNames: () => useCaseSensitiveFileNames, getNewLine: () => newLine, - getModuleResolutionHost: () => moduleResolutionHost + fileExists: fileName => getSourceFile(fileName, ts.ScriptTarget.ES5) !== undefined, + readFile: (fileName: string): string => { throw new Error("NotYetImplemented"); } }; } diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 5314d877648..e989eee77a9 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -230,7 +230,10 @@ module Harness.LanguageService { throw new Error("NYI"); } fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; } - + readFile(fileName: string) { + let snapshot = this.nativeHost.getScriptSnapshot(fileName); + return snapshot && snapshot.getText(0, snapshot.getLength()); + } log(s: string): void { this.nativeHost.log(s); } trace(s: string): void { this.nativeHost.trace(s); } error(s: string): void { this.nativeHost.error(s); } diff --git a/src/harness/projectsRunner.ts b/src/harness/projectsRunner.ts index 2d7febe0b67..74989331c44 100644 --- a/src/harness/projectsRunner.ts +++ b/src/harness/projectsRunner.ts @@ -129,10 +129,6 @@ class ProjectRunner extends RunnerBase { getSourceFileText: (fileName: string) => string, writeFile: (fileName: string, data: string, writeByteOrderMark: boolean) => void): CompileProjectFilesResult { - let moduleResolutionHost: ts.ModuleResolutionHost = { - fileExists: fileName => getSourceFile(fileName, ts.ScriptTarget.ES5) !== undefined, - } - let program = ts.createProgram(getInputFiles(), createCompilerOptions(), createCompilerHost()); let errors = ts.getPreEmitDiagnostics(program); @@ -196,7 +192,8 @@ class ProjectRunner extends RunnerBase { getCanonicalFileName: Harness.Compiler.getCanonicalFileName, useCaseSensitiveFileNames: () => ts.sys.useCaseSensitiveFileNames, getNewLine: () => ts.sys.newLine, - getModuleResolutionHost: () => moduleResolutionHost + fileExists: fileName => getSourceFile(fileName, ts.ScriptTarget.ES5) !== undefined, + readFile: fileName => Harness.IO.readFile(fileName) }; } } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 3d566594ed0..578c9cce553 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -94,12 +94,13 @@ namespace ts.server { constructor(public host: ServerHost, public project: Project) { this.resolvedModuleNames = ts.createFileMap>(ts.createGetCanonicalFileName(host.useCaseSensitiveFileNames)) this.moduleResolutionHost = { - fileExists: fileName => this.fileExists(fileName) + fileExists: fileName => this.fileExists(fileName), + readFile: fileName => this.host.readFile(fileName) } } resolveModuleNames(moduleNames: string[], containingFile: string): string[] { - let currentResolutionsInFile = this.resolvedModuleNames.get(containingFile); + let currentResolutionsInFile = this.resolvedModuleNames.get(containingFile); let newResolutions: Map = {}; let resolvedFileNames: string[] = []; diff --git a/src/services/services.ts b/src/services/services.ts index c0a617646b8..a3b31afabf0 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -73,7 +73,7 @@ namespace ts { } /** - * Represents an immutable snapshot of a script at a specified time.Once acquired, the + * Represents an immutable snapshot of a script at a specified time.Once acquired, the * snapshot is observably immutable. i.e. the same calls with the same parameters will return * the same values. */ @@ -85,9 +85,9 @@ namespace ts { getLength(): number; /** - * Gets the TextChangeRange that describe how the text changed between this text and + * Gets the TextChangeRange that describe how the text changed between this text and * an older version. This information is used by the incremental parser to determine - * what sections of the script need to be re-parsed. 'undefined' can be returned if the + * what sections of the script need to be re-parsed. 'undefined' can be returned if the * change range cannot be determined. However, in that case, incremental parsing will * not happen and the entire document will be re - parsed. */ @@ -276,8 +276,8 @@ namespace ts { let children = this.getChildren(sourceFile); let child = lastOrUndefined(children); - if (!child) { - return undefined; + if (!child) { + return undefined; } return child.kind < SyntaxKind.FirstNode ? child : child.getLastToken(sourceFile); @@ -337,10 +337,10 @@ namespace ts { ts.forEach(declarations, (declaration, indexOfDeclaration) => { // Make sure we are collecting doc comment from declaration once, - // In case of union property there might be same declaration multiple times + // In case of union property there might be same declaration multiple times // which only varies in type parameter // Eg. let a: Array | Array; a.length - // The property length will have two declarations of property length coming + // The property length will have two declarations of property length coming // from Array - Array and Array if (indexOf(declarations, declaration) === indexOfDeclaration) { let sourceFileOfDeclaration = getSourceFileOfNode(declaration); @@ -362,7 +362,7 @@ namespace ts { // If this is dotted module name, get the doc comments from the parent while (declaration.kind === SyntaxKind.ModuleDeclaration && declaration.parent.kind === SyntaxKind.ModuleDeclaration) { declaration = declaration.parent; - } + } // Get the cleaned js doc comment text from the declaration ts.forEach(getJsDocCommentTextRange( @@ -382,7 +382,7 @@ namespace ts { jsDocComment => { return { pos: jsDocComment.pos + "/*".length, // Consume /* from the comment - end: jsDocComment.end - "*/".length // Trim off comment end indicator + end: jsDocComment.end - "*/".length // Trim off comment end indicator }; }); } @@ -487,7 +487,7 @@ namespace ts { pushDocCommentLineText(docComments, docCommentTextOfLine, blankLineCount); blankLineCount = 0; } - else if (!isInParamTag && docComments.length) { + else if (!isInParamTag && docComments.length) { // This is blank line when there is text already parsed blankLineCount++; } @@ -503,7 +503,7 @@ namespace ts { if (isParamTag(pos, end, sourceFile)) { let blankLineCount = 0; let recordedParamTag = false; - // Consume leading spaces + // Consume leading spaces pos = consumeWhiteSpaces(pos + paramTag.length); if (pos >= end) { break; @@ -561,7 +561,7 @@ namespace ts { while (pos < end) { let ch = sourceFile.text.charCodeAt(pos); - // at line break, set this comment line text and go to next line + // at line break, set this comment line text and go to next line if (isLineBreak(ch)) { if (paramHelpString) { pushDocCommentLineText(paramDocComments, paramHelpString, blankLineCount); @@ -627,7 +627,7 @@ namespace ts { paramHelpStringMargin = sourceFile.getLineAndCharacterOfPosition(firstLineParamHelpStringPos).character; } - // Now consume white spaces max + // Now consume white spaces max let startOfLinePos = pos; pos = consumeWhiteSpacesOnTheLine(pos, end, sourceFile, paramHelpStringMargin); if (pos >= end) { @@ -976,13 +976,12 @@ namespace ts { trace? (s: string): void; error? (s: string): void; useCaseSensitiveFileNames? (): boolean; - - // LS host can implement one of these methods - // if getModuleResolutionHost is implemented then compiler will apply one of built-in ways to resolve module names - // and ModuleResolutionHost will be used to ask host specific questions - // if resolveModuleName is implemented - this will mean that host is completely in charge of module name resolution - // if none of these methods are implemented then language service will try to emulate getModuleResolutionHost atop of 'getScriptSnapshot' - getModuleResolutionHost?(): ModuleResolutionHost; + + /* + * LS host can optionally implement this method if it wants to be completely in charge of module name resolution. + * if implementation is omitted then language service will use built-in module resolution logic and get answers to + * host specific questions using 'getScriptSnapshot'. + */ resolveModuleNames?(moduleNames: string[], containingFile: string): string[]; } @@ -1000,17 +999,17 @@ namespace ts { // diagnostics present for the program level, and not just 'options' diagnostics. getCompilerOptionsDiagnostics(): Diagnostic[]; - /** + /** * @deprecated Use getEncodedSyntacticClassifications instead. */ getSyntacticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; - /** + /** * @deprecated Use getEncodedSemanticClassifications instead. */ getSemanticClassifications(fileName: string, span: TextSpan): ClassifiedSpan[]; - // Encoded as triples of [start, length, ClassificationType]. + // Encoded as triples of [start, length, ClassificationType]. getEncodedSyntacticClassifications(fileName: string, span: TextSpan): Classifications; getEncodedSemanticClassifications(fileName: string, span: TextSpan): Classifications; @@ -1229,7 +1228,7 @@ namespace ts { * Represents a single signature to show in signature help. * The id is used for subsequent calls into the language service to ask questions about the * signature help item in the context of any documents that have been updated. i.e. after - * an edit has happened, while signature help is still active, the host can ask important + * an edit has happened, while signature help is still active, the host can ask important * questions like 'what parameter is the user currently contained within?'. */ export interface SignatureHelpItem { @@ -1283,8 +1282,8 @@ namespace ts { /** The text to display in the editor for the collapsed region. */ bannerText: string; - /** - * Whether or not this region should be automatically collapsed when + /** + * Whether or not this region should be automatically collapsed when * the 'Collapse to Definitions' command is invoked. */ autoCollapse: boolean; @@ -1365,18 +1364,18 @@ namespace ts { } /** - * The document registry represents a store of SourceFile objects that can be shared between + * The document registry represents a store of SourceFile objects that can be shared between * multiple LanguageService instances. A LanguageService instance holds on the SourceFile (AST) - * of files in the context. - * SourceFile objects account for most of the memory usage by the language service. Sharing - * the same DocumentRegistry instance between different instances of LanguageService allow - * for more efficient memory utilization since all projects will share at least the library + * of files in the context. + * SourceFile objects account for most of the memory usage by the language service. Sharing + * the same DocumentRegistry instance between different instances of LanguageService allow + * for more efficient memory utilization since all projects will share at least the library * file (lib.d.ts). * - * A more advanced use of the document registry is to serialize sourceFile objects to disk + * A more advanced use of the document registry is to serialize sourceFile objects to disk * and re-hydrate them when needed. * - * To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it + * To create a default DocumentRegistry, use createDocumentRegistry to create one, and pass it * to all subsequent createLanguageService calls. */ export interface DocumentRegistry { @@ -1386,7 +1385,7 @@ namespace ts { * the SourceFile if was not found in the registry. * * @param fileName The name of the file requested - * @param compilationSettings Some compilation settings like target affects the + * @param compilationSettings Some compilation settings like target affects the * shape of a the resulting SourceFile. This allows the DocumentRegistry to store * multiple copies of the same file for different compilation settings. * @parm scriptSnapshot Text of the file. Only used if the file was not found @@ -1406,10 +1405,10 @@ namespace ts { * to get an updated SourceFile. * * @param fileName The name of the file requested - * @param compilationSettings Some compilation settings like target affects the + * @param compilationSettings Some compilation settings like target affects the * shape of a the resulting SourceFile. This allows the DocumentRegistry to store * multiple copies of the same file for different compilation settings. - * @param scriptSnapshot Text of the file. + * @param scriptSnapshot Text of the file. * @param version Current version of the file. */ updateDocument( @@ -1588,7 +1587,7 @@ namespace ts { sourceFile: SourceFile; // The number of language services that this source file is referenced in. When no more - // language services are referencing the file, then the file can be removed from the + // language services are referencing the file, then the file can be removed from the // registry. languageServiceRefCount: number; owners: string[]; @@ -1643,8 +1642,8 @@ namespace ts { }; } - // Cache host information about scrip Should be refreshed - // at each language service public entry point, since we don't know when + // Cache host information about scrip Should be refreshed + // at each language service public entry point, since we don't know when // set of scripts handled by the host changes. class HostCache { private fileNameToEntry: FileMap; @@ -1723,8 +1722,8 @@ namespace ts { } class SyntaxTreeCache { - // For our syntactic only features, we also keep a cache of the syntax tree for the - // currently edited file. + // For our syntactic only features, we also keep a cache of the syntax tree for the + // currently edited file. private currentFileName: string; private currentFileVersion: string; private currentFileScriptSnapshot: IScriptSnapshot; @@ -1769,20 +1768,20 @@ namespace ts { sourceFile.version = version; sourceFile.scriptSnapshot = scriptSnapshot; } - + export interface TranspileOptions { compilerOptions?: CompilerOptions; fileName?: string; reportDiagnostics?: boolean; moduleName?: string; } - + export interface TranspileOutput { outputText: string; diagnostics?: Diagnostic[]; sourceMapText?: string; } - + /* * This function will compile source text from 'input' argument using specified compiler options. * If not options are provided - it will use a set of default compiler options. @@ -1791,7 +1790,7 @@ namespace ts { * - allowNonTsExtensions = true * - noLib = true * - noResolve = true - */ + */ export function transpileModule(input: string, transpileOptions?: TranspileOptions): TranspileOutput { let options = transpileOptions.compilerOptions ? clone(transpileOptions.compilerOptions) : getDefaultCompilerOptions(); @@ -1800,7 +1799,7 @@ namespace ts { // Filename can be non-ts file. options.allowNonTsExtensions = true; - // We are not returning a sourceFile for lib file when asked by the program, + // We are not returning a sourceFile for lib file when asked by the program, // so pass --noLib to avoid reporting a file not found error. options.noLib = true; @@ -1820,9 +1819,6 @@ namespace ts { // Output let outputText: string; let sourceMapText: string; - let moduleResolutionHost: ModuleResolutionHost = { - fileExists: fileName => fileName === inputFileName, - } // Create a compilerHost object to allow the compiler to read and write files let compilerHost: CompilerHost = { getSourceFile: (fileName, target) => fileName === inputFileName ? sourceFile : undefined, @@ -1841,11 +1837,13 @@ namespace ts { getCanonicalFileName: fileName => fileName, getCurrentDirectory: () => "", getNewLine: () => newLine, - getModuleResolutionHost: () => moduleResolutionHost + + fileExists: (fileName): boolean => { throw new Error("Should never be called."); }, + readFile: (fileName): string => { throw new Error("Should never be called."); } }; let program = createProgram([inputFileName], options, compilerHost); - + let diagnostics: Diagnostic[]; if (transpileOptions.reportDiagnostics) { diagnostics = []; @@ -1857,11 +1855,11 @@ namespace ts { Debug.assert(outputText !== undefined, "Output generation failed"); - return { outputText, diagnostics, sourceMapText }; + return { outputText, diagnostics, sourceMapText }; } /* - * This is a shortcut function for transpileModule - it accepts transpileOptions as parameters and returns only outputText part of the result. + * This is a shortcut function for transpileModule - it accepts transpileOptions as parameters and returns only outputText part of the result. */ export function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string { let output = transpileModule(input, { compilerOptions, fileName, reportDiagnostics: !!diagnostics, moduleName }); @@ -1882,19 +1880,19 @@ namespace ts { export let disableIncrementalParsing = false; export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile { - // If we were given a text change range, and our version or open-ness changed, then + // If we were given a text change range, and our version or open-ness changed, then // incrementally parse this file. if (textChangeRange) { if (version !== sourceFile.version) { // Once incremental parsing is ready, then just call into this function. if (!disableIncrementalParsing) { let newText: string; - + // grab the fragment from the beginning of the original text to the beginning of the span let prefix = textChangeRange.span.start !== 0 ? sourceFile.text.substr(0, textChangeRange.span.start) : ""; - + // grab the fragment from the end of the span till the end of the original text let suffix = textSpanEnd(textChangeRange.span) !== sourceFile.text.length ? sourceFile.text.substr(textSpanEnd(textChangeRange.span)) @@ -1908,10 +1906,10 @@ namespace ts { // it was actual edit, fetch the fragment of new text that correspond to new span let changedText = scriptSnapshot.getText(textChangeRange.span.start, textChangeRange.span.start + textChangeRange.newLength); // combine prefix, changed text and suffix - newText = prefix && suffix + newText = prefix && suffix ? prefix + changedText + suffix : prefix - ? (prefix + changedText) + ? (prefix + changedText) : (changedText + suffix); } @@ -1953,7 +1951,7 @@ namespace ts { let getCanonicalFileName = createGetCanonicalFileName(!!useCaseSensitiveFileNames); function getKeyFromCompilationSettings(settings: CompilerOptions): string { - return "_" + settings.target + "|" + settings.module + "|" + settings.noResolve; + return "_" + settings.target + "|" + settings.module + "|" + settings.noResolve + "|" + settings.jsx; } function getBucketForCompilationSettings(settings: CompilerOptions, createIfMissing: boolean): FileMap { @@ -2017,7 +2015,7 @@ namespace ts { bucket.set(fileName, entry); } else { - // We have an entry for this file. However, it may be for a different version of + // We have an entry for this file. However, it may be for a different version of // the script snapshot. If so, update it appropriately. Otherwise, we can just // return it as is. if (entry.sourceFile.version !== version) { @@ -2058,7 +2056,7 @@ namespace ts { reportStats }; } - + export function resolveModuleName(fileName: string, moduleName: string, compilerOptions: CompilerOptions, host: ModuleResolutionHost): ResolvedModule { let resolver = getDefaultModuleNameResolver(compilerOptions); return resolver(moduleName, fileName, compilerOptions, host); @@ -2084,7 +2082,7 @@ namespace ts { } }); } - + function recordAmbientExternalModule(): void { if (!ambientExternalModules) { ambientExternalModules = []; @@ -2556,17 +2554,20 @@ namespace ts { return; } - // IMPORTANT - It is critical from this moment onward that we do not check + // IMPORTANT - It is critical from this moment onward that we do not check // cancellation tokens. We are about to mutate source files from a previous program // instance. If we cancel midway through, we may end up in an inconsistent state where - // the program points to old source files that have been invalidated because of + // the program points to old source files that have been invalidated because of // incremental parsing. let oldSettings = program && program.getCompilerOptions(); let newSettings = hostCache.compilationSettings(); - let changesInCompilationSettingsAffectSyntax = oldSettings && - (oldSettings.target !== newSettings.target || oldSettings.module !== newSettings.module || oldSettings.noResolve !== newSettings.noResolve); - + let changesInCompilationSettingsAffectSyntax = oldSettings && + (oldSettings.target !== newSettings.target || + oldSettings.module !== newSettings.module || + oldSettings.noResolve !== newSettings.noResolve || + oldSettings.jsx !== newSettings.jsx); + // Now create a new compiler let compilerHost: CompilerHost = { getSourceFile: getOrCreateSourceFile, @@ -2577,26 +2578,25 @@ namespace ts { getDefaultLibFileName: (options) => host.getDefaultLibFileName(options), writeFile: (fileName, data, writeByteOrderMark) => { }, getCurrentDirectory: () => host.getCurrentDirectory(), + fileExists: (fileName): boolean => { throw new Error("Not implemented"); }, + readFile: (fileName): string => { throw new Error("Not implemented"); } }; - + if (host.resolveModuleNames) { compilerHost.resolveModuleNames = (moduleNames, containingFile) => host.resolveModuleNames(moduleNames, containingFile) } - else if (host.getModuleResolutionHost) { - compilerHost.getModuleResolutionHost = () => host.getModuleResolutionHost() - } else { - compilerHost.getModuleResolutionHost = () => { - // stub missing host functionality - return { - fileExists: fileName => hostCache.getOrCreateEntry(fileName) != undefined, - }; + // stub missing host functionality + compilerHost.fileExists = fileName => hostCache.getOrCreateEntry(fileName) !== undefined; + compilerHost.readFile = fileName => { + let entry = hostCache.getOrCreateEntry(fileName); + return entry && entry.scriptSnapshot.getText(0, entry.scriptSnapshot.getLength()); } } - + let newProgram = createProgram(hostCache.getRootFileNames(), newSettings, compilerHost, program); - // Release any files we have acquired in the old program but are + // Release any files we have acquired in the old program but are // not part of the new program. if (program) { let oldSourceFiles = program.getSourceFiles(); @@ -2614,7 +2614,7 @@ namespace ts { program = newProgram; - // Make sure all the nodes in the program are both bound, and have their parent + // Make sure all the nodes in the program are both bound, and have their parent // pointers set property. program.getTypeChecker(); return; @@ -2636,7 +2636,7 @@ namespace ts { // Check if the old program had this file already let oldSourceFile = program && program.getSourceFile(fileName); if (oldSourceFile) { - // We already had a source file for this file name. Go to the registry to + // We already had a source file for this file name. Go to the registry to // ensure that we get the right up to date version of it. We need this to // address the following 'race'. Specifically, say we have the following: // @@ -2647,15 +2647,15 @@ namespace ts { // LS2 // // Each LS has a reference to file 'foo.ts' at version 1. LS2 then updates - // it's version of 'foo.ts' to version 2. This will cause LS2 and the - // DocumentRegistry to have version 2 of the document. HOwever, LS1 will + // it's version of 'foo.ts' to version 2. This will cause LS2 and the + // DocumentRegistry to have version 2 of the document. HOwever, LS1 will // have version 1. And *importantly* this source file will be *corrupt*. // The act of creating version 2 of the file irrevocably damages the version // 1 file. // // So, later when we call into LS1, we need to make sure that it doesn't use // it's source file any more, and instead defers to DocumentRegistry to get - // either version 1, version 2 (or some other version) depending on what the + // either version 1, version 2 (or some other version) depending on what the // host says should be used. return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); } @@ -2721,7 +2721,7 @@ namespace ts { /** * getSemanticDiagnostiscs return array of Diagnostics. If '-d' is not enabled, only report semantic errors - * If '-d' enabled, report both semantic and emitter errors + * If '-d' enabled, report both semantic and emitter errors */ function getSemanticDiagnostics(fileName: string): Diagnostic[] { synchronizeHostData(); @@ -2729,7 +2729,7 @@ namespace ts { let targetSourceFile = getValidSourceFile(fileName); // For JavaScript files, we don't want to report the normal typescript semantic errors. - // Instead, we just report errors for using TypeScript-only constructs from within a + // Instead, we just report errors for using TypeScript-only constructs from within a // JavaScript file. if (isJavaScript(fileName)) { return getJavaScriptSemanticDiagnostics(targetSourceFile); @@ -3109,7 +3109,7 @@ namespace ts { } if (isJavaScriptFile && type.flags & TypeFlags.Union) { - // In javascript files, for union types, we don't just get the members that + // In javascript files, for union types, we don't just get the members that // the individual types have in common, we also include all the members that // each individual type has. This is because we're going to add all identifiers // anyways. So we might as well elevate the members that were at least part @@ -3164,10 +3164,10 @@ namespace ts { // aggregating completion candidates. This is achieved in 'getScopeNode' // by finding the first node that encompasses a position, accounting for whether a node // is "complete" to decide whether a position belongs to the node. - // + // // However, at the end of an identifier, we are interested in the scope of the identifier // itself, but fall outside of the identifier. For instance: - // + // // xyz => x$ // // the cursor is outside of both the 'x' and the arrow function 'xyz => x', @@ -3194,7 +3194,7 @@ namespace ts { /// TODO filter meaning based on the current context let symbolMeanings = SymbolFlags.Type | SymbolFlags.Value | SymbolFlags.Namespace | SymbolFlags.Alias; symbols = typeChecker.getSymbolsInScope(scopeNode, symbolMeanings); - + return true; } @@ -3332,7 +3332,7 @@ namespace ts { let rootDeclaration = getRootDeclaration(objectLikeContainer.parent); if (isVariableLike(rootDeclaration)) { - // We don't want to complete using the type acquired by the shape + // We don't want to complete using the type acquired by the shape // of the binding pattern; we are only interested in types acquired // through type declaration or inference. if (rootDeclaration.initializer || rootDeclaration.type) { @@ -3457,8 +3457,8 @@ namespace ts { // its parent is a JsxExpression, whose parent is a JsxAttribute, // whose parent is a JsxOpeningLikeElement if(parent && - parent.kind === SyntaxKind.JsxExpression && - parent.parent && + parent.kind === SyntaxKind.JsxExpression && + parent.parent && parent.parent.kind === SyntaxKind.JsxAttribute) { return parent.parent.parent; @@ -3503,16 +3503,16 @@ namespace ts { containingNodeKind === SyntaxKind.FunctionDeclaration || // function A !lookUp(existingMemberNames, m.name)); } - + /** * Filters out completion suggestions from 'symbols' according to existing JSX attributes. * @@ -3747,7 +3747,7 @@ namespace ts { } function createCompletionEntry(symbol: Symbol, location: Node): CompletionEntry { - // Try to get a valid display name for this symbol, if we could not find one, then ignore it. + // Try to get a valid display name for this symbol, if we could not find one, then ignore it. // We would like to only show things that can be added after a dot, so for instance numeric properties can // not be accessed with a dot (a.1 <- invalid) let displayName = getCompletionEntryDisplayNameForSymbol(symbol, program.getCompilerOptions().target, /*performCharacterChecks:*/ true, location); @@ -3755,10 +3755,10 @@ namespace ts { return undefined; } - // TODO(drosen): Right now we just permit *all* semantic meanings when calling - // 'getSymbolKind' which is permissible given that it is backwards compatible; but + // TODO(drosen): Right now we just permit *all* semantic meanings when calling + // 'getSymbolKind' which is permissible given that it is backwards compatible; but // really we should consider passing the meaning for the node so that we don't report - // that a suggestion for a value is an interface. We COULD also just do what + // that a suggestion for a value is an interface. We COULD also just do what // 'getSymbolModifiers' does, which is to use the first declaration. // Use a 'sortText' of 0' so that all symbol completion entries come before any other @@ -3804,8 +3804,8 @@ namespace ts { // Find the symbol with the matching entry name. let target = program.getCompilerOptions().target; - // We don't need to perform character checks here because we're only comparing the - // name against 'entryName' (which is known to be good), not building a new + // We don't need to perform character checks here because we're only comparing the + // name against 'entryName' (which is known to be good), not building a new // completion entry. let symbol = forEach(symbols, s => getCompletionEntryDisplayNameForSymbol(s, target, /*performCharacterChecks:*/ false, location) === entryName ? s : undefined); @@ -3820,7 +3820,7 @@ namespace ts { }; } } - + // Didn't find a symbol with this name. See if we can find a keyword instead. let keywordCompletion = forEach(keywordCompletions, c => c.name === entryName); if (keywordCompletion) { @@ -3896,7 +3896,7 @@ namespace ts { Debug.assert(!!(rootSymbolFlags & SymbolFlags.Method)); }); if (!unionPropertyKind) { - // If this was union of all methods, + // If this was union of all methods, //make sure it has call signatures before we can label it as method let typeOfUnionProperty = typeChecker.getTypeOfSymbolAtLocation(symbol, location); if (typeOfUnionProperty.getCallSignatures().length) { @@ -3970,7 +3970,7 @@ namespace ts { let allSignatures = useConstructSignatures ? type.getConstructSignatures() : type.getCallSignatures(); if (!contains(allSignatures, signature.target || signature)) { - // Get the first signature if there + // Get the first signature if there signature = allSignatures.length ? allSignatures[0] : undefined; } @@ -4346,7 +4346,7 @@ namespace ts { if (!tryAddConstructSignature(symbol, node, symbolKind, symbolName, containerName, result) && !tryAddCallSignature(symbol, node, symbolKind, symbolName, containerName, result)) { - // Just add all the declarations. + // Just add all the declarations. forEach(declarations, declaration => { result.push(createDefinitionInfo(declaration, symbolKind, symbolName, containerName)); }); @@ -4457,7 +4457,7 @@ namespace ts { // Because name in short-hand property assignment has two different meanings: property name and property value, // using go-to-definition at such position should go to the variable declaration of the property value rather than - // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition + // go to the declaration of the property name (in this case stay at the same position). However, if go-to-definition // is performed at the location of property access, we would like to go to definition of the property in the short-hand // assignment. This case and others are handled by the following code. if (node.parent.kind === SyntaxKind.ShorthandPropertyAssignment) { @@ -4523,7 +4523,7 @@ namespace ts { if (results) { let sourceFile = getCanonicalFileName(normalizeSlashes(fileName)); - // Get occurrences only supports reporting occurrences for the file queried. So + // Get occurrences only supports reporting occurrences for the file queried. So // filter down to that list. results = filter(results, r => getCanonicalFileName(ts.normalizeSlashes(r.fileName)) === sourceFile); } @@ -4750,7 +4750,7 @@ namespace ts { if (isFunctionBlock(parent) || parent.kind === SyntaxKind.SourceFile) { return parent; } - + // A throw-statement is only owned by a try-statement if the try-statement has // a catch clause, and if the throw-statement occurs within the try block. if (parent.kind === SyntaxKind.TryStatement) { @@ -4844,7 +4844,7 @@ namespace ts { return undefined; } } - else { + else { // unsupported modifier return undefined; } @@ -5440,13 +5440,13 @@ namespace ts { // If we are past the end, stop looking if (position > end) break; - // We found a match. Make sure it's not part of a larger word (i.e. the char + // We found a match. Make sure it's not part of a larger word (i.e. the char // before and after it have to be a non-identifier char). let endPosition = position + symbolNameLength; if ((position === 0 || !isIdentifierPart(text.charCodeAt(position - 1), ScriptTarget.Latest)) && (endPosition === sourceLength || !isIdentifierPart(text.charCodeAt(endPosition), ScriptTarget.Latest))) { - // Found a real match. Keep searching. + // Found a real match. Keep searching. positions.push(position); } position = text.indexOf(symbolName, position + symbolNameLength + 1); @@ -5513,9 +5513,9 @@ namespace ts { return false; } - /** Search within node "container" for references for a search value, where the search value is defined as a + /** Search within node "container" for references for a search value, where the search value is defined as a * tuple of(searchSymbol, searchText, searchLocation, and searchMeaning). - * searchLocation: a node where the search value + * searchLocation: a node where the search value */ function getReferencesInNode(container: Node, searchSymbol: Symbol, @@ -5541,7 +5541,7 @@ namespace ts { let referenceLocation = getTouchingPropertyName(sourceFile, position); if (!isValidReferencePosition(referenceLocation, searchText)) { - // This wasn't the start of a token. Check to see if it might be a + // This wasn't the start of a token. Check to see if it might be a // match in a comment or string if that's what the caller is asking // for. if ((findInStrings && isInString(position)) || @@ -5883,7 +5883,7 @@ namespace ts { } } - // If the reference location is in an object literal, try to get the contextual type for the + // If the reference location is in an object literal, try to get the contextual type for the // object literal, lookup the property symbol in the contextual type, and use this symbol to // compare to our searchSymbol if (isNameOfPropertyAssignment(referenceLocation)) { @@ -5900,7 +5900,7 @@ namespace ts { return rootSymbol; } - // Finally, try all properties with the same name in any type the containing type extended or implemented, and + // Finally, try all properties with the same name in any type the containing type extended or implemented, and // see if any is in the list if (rootSymbol.parent && rootSymbol.parent.flags & (SymbolFlags.Class | SymbolFlags.Interface)) { let result: Symbol[] = []; @@ -6251,7 +6251,7 @@ namespace ts { } else if (isNameOfModuleDeclaration(nodeForStartPos)) { // If this is name of a module declarations, check if this is right side of dotted module name - // If parent of the module declaration which is parent of this node is module declaration and its body is the module declaration that this node is name of + // If parent of the module declaration which is parent of this node is module declaration and its body is the module declaration that this node is name of // Then this name is name from dotted module if (nodeForStartPos.parent.parent.kind === SyntaxKind.ModuleDeclaration && (nodeForStartPos.parent.parent).body === nodeForStartPos.parent) { @@ -6294,7 +6294,7 @@ namespace ts { // been canceled. That would be an enormous amount of chattyness, along with the all // the overhead of marshalling the data to/from the host. So instead we pick a few // reasonable node kinds to bother checking on. These node kinds represent high level - // constructs that we would expect to see commonly, but just at a far less frequent + // constructs that we would expect to see commonly, but just at a far less frequent // interval. // // For example, in checker.ts (around 750k) we only have around 600 of these constructs. @@ -6367,7 +6367,7 @@ namespace ts { */ function hasValueSideModule(symbol: Symbol): boolean { return forEach(symbol.declarations, declaration => { - return declaration.kind === SyntaxKind.ModuleDeclaration && + return declaration.kind === SyntaxKind.ModuleDeclaration && getModuleInstanceState(declaration) === ModuleInstanceState.Instantiated; }); } @@ -6488,8 +6488,8 @@ namespace ts { // Only bother with the trivia if it at least intersects the span of interest. if (isComment(kind)) { classifyComment(token, kind, start, width); - - // Classifying a comment might cause us to reuse the trivia scanner + + // Classifying a comment might cause us to reuse the trivia scanner // (because of jsdoc comments). So after we classify the comment make // sure we set the scanner position back to where it needs to be. triviaScanner.setTextPos(end); @@ -6540,7 +6540,7 @@ namespace ts { for (let tag of docComment.tags) { // As we walk through each tag, classify the portion of text from the end of - // the last tag (or the start of the entire doc comment) as 'comment'. + // the last tag (or the start of the entire doc comment) as 'comment'. if (tag.pos !== pos) { pushCommentRange(pos, tag.pos - pos); } @@ -6602,7 +6602,7 @@ namespace ts { } function classifyDisabledMergeCode(text: string, start: number, end: number) { - // Classify the line that the ======= marker is on as a comment. Then just lex + // Classify the line that the ======= marker is on as a comment. Then just lex // all further tokens and add them to the result. for (var i = start; i < end; i++) { if (isLineBreak(text.charCodeAt(i))) { @@ -6645,7 +6645,7 @@ namespace ts { } } - // for accurate classification, the actual token should be passed in. however, for + // for accurate classification, the actual token should be passed in. however, for // cases like 'disabled merge code' classification, we just get the token kind and // classify based on that instead. function classifyTokenType(tokenKind: SyntaxKind, token?: Node): ClassificationType { @@ -6860,11 +6860,11 @@ namespace ts { } function getTodoComments(fileName: string, descriptors: TodoCommentDescriptor[]): TodoComment[] { - // Note: while getting todo comments seems like a syntactic operation, we actually + // Note: while getting todo comments seems like a syntactic operation, we actually // treat it as a semantic operation here. This is because we expect our host to call // this on every single file. If we treat this syntactically, then that will cause // us to populate and throw away the tree in our syntax tree cache for each file. By - // treating this as a semantic operation, we can access any tree without throwing + // treating this as a semantic operation, we can access any tree without throwing // anything away. synchronizeHostData(); @@ -6894,7 +6894,7 @@ namespace ts { // 0) The full match for the entire regexp. // 1) The preamble to the message portion. // 2) The message portion. - // 3...N) The descriptor that was matched - by index. 'undefined' for each + // 3...N) The descriptor that was matched - by index. 'undefined' for each // descriptor that didn't match. an actual value if it did match. // // i.e. 'undefined' in position 3 above means TODO(jason) didn't match. @@ -6920,7 +6920,7 @@ namespace ts { } Debug.assert(descriptor !== undefined); - // We don't want to match something like 'TODOBY', so we make sure a non + // We don't want to match something like 'TODOBY', so we make sure a non // letter/digit follows the match. if (isLetterOrDigit(fileContents.charCodeAt(matchPosition + descriptor.text.length))) { continue; @@ -6972,11 +6972,11 @@ namespace ts { // (?:(TODO\(jason\))|(HACK)) // // Note that the outermost group is *not* a capture group, but the innermost groups - // *are* capture groups. By capturing the inner literals we can determine after + // *are* capture groups. By capturing the inner literals we can determine after // matching which descriptor we are dealing with. let literals = "(?:" + map(descriptors, d => "(" + escapeRegExp(d.text) + ")").join("|") + ")"; - // After matching a descriptor literal, the following regexp matches the rest of the + // After matching a descriptor literal, the following regexp matches the rest of the // text up to the end of the line (or */). let endOfLineOrEndOfComment = /(?:$|\*\/)/.source let messageRemainder = /(?:.*?)/.source @@ -7158,7 +7158,7 @@ namespace ts { /// We do not have a full parser support to know when we should parse a regex or not /// If we consider every slash token to be a regex, we could be missing cases like "1/2/3", where - /// we have a series of divide operator. this list allows us to be more accurate by ruling out + /// we have a series of divide operator. this list allows us to be more accurate by ruling out /// locations where a regexp cannot exist. let noRegexTable: boolean[] = []; noRegexTable[SyntaxKind.Identifier] = true; @@ -7204,7 +7204,7 @@ namespace ts { keyword2 === SyntaxKind.ConstructorKeyword || keyword2 === SyntaxKind.StaticKeyword) { - // Allow things like "public get", "public constructor" and "public static". + // Allow things like "public get", "public constructor" and "public static". // These are all legal. return true; } @@ -7275,7 +7275,7 @@ namespace ts { function getClassificationsForLine(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): ClassificationResult { return convertClassifications(getEncodedLexicalClassifications(text, lexState, syntacticClassifierAbsent), text); } - + // If there is a syntactic classifier ('syntacticClassifierAbsent' is false), // we will be more conservative in order to avoid conflicting with the syntactic classifier. function getEncodedLexicalClassifications(text: string, lexState: EndOfLineState, syntacticClassifierAbsent: boolean): Classifications { @@ -7337,12 +7337,12 @@ namespace ts { // token. So the classification will go back to being an identifier. The moment the user // types again, number will become a keyword, then an identifier, etc. etc. // - // To try to avoid this problem, we avoid classifying contextual keywords as keywords + // To try to avoid this problem, we avoid classifying contextual keywords as keywords // when the user is potentially typing something generic. We just can't do a good enough // job at the lexical level, and so well leave it up to the syntactic classifier to make // the determination. // - // In order to determine if the user is potentially typing something generic, we use a + // In order to determine if the user is potentially typing something generic, we use a // weak heuristic where we track < and > tokens. It's a weak heuristic, but should // work well enough in practice. let angleBracketStack = 0; @@ -7360,7 +7360,7 @@ namespace ts { token = SyntaxKind.Identifier; } else if (isKeyword(lastNonTriviaToken) && isKeyword(token) && !canFollow(lastNonTriviaToken, token)) { - // We have two keywords in a row. Only treat the second as a keyword if + // We have two keywords in a row. Only treat the second as a keyword if // it's a sequence that could legally occur in the language. Otherwise // treat it as an identifier. This way, if someone writes "private var" // we recognize that 'var' is actually an identifier here. @@ -7368,7 +7368,7 @@ namespace ts { } else if (lastNonTriviaToken === SyntaxKind.Identifier && token === SyntaxKind.LessThanToken) { - // Could be the start of something generic. Keep track of that by bumping + // Could be the start of something generic. Keep track of that by bumping // up the current count of generic contexts we may be in. angleBracketStack++; } @@ -7383,7 +7383,7 @@ namespace ts { token === SyntaxKind.BooleanKeyword || token === SyntaxKind.SymbolKeyword) { if (angleBracketStack > 0 && !syntacticClassifierAbsent) { - // If it looks like we're could be in something generic, don't classify this + // If it looks like we're could be in something generic, don't classify this // as a keyword. We may just get overwritten by the syntactic classifier, // causing a noisy experience for the user. token = SyntaxKind.Identifier; @@ -7491,8 +7491,8 @@ namespace ts { } if (start === 0 && offset > 0) { - // We're classifying the first token, and this was a case where we prepended - // text. We should consider the start of this token to be at the start of + // We're classifying the first token, and this was a case where we prepended + // text. We should consider the start of this token to be at the start of // the original text. start += offset; } @@ -7615,11 +7615,11 @@ namespace ts { /// getDefaultLibraryFilePath declare let __dirname: string; - + /** * Get the path of the default library files (lib.d.ts) as distributed with the typescript * node package. - * The functionality is not supported if the ts module is consumed outside of a node module. + * The functionality is not supported if the ts module is consumed outside of a node module. */ export function getDefaultLibFilePath(options: CompilerOptions): string { // Check __dirname is defined and that we are on a node.js system. diff --git a/src/services/shims.ts b/src/services/shims.ts index ace7fbeff01..f203dc6cbc4 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -421,6 +421,10 @@ namespace ts { public fileExists(fileName: string): boolean { return this.shimHost.fileExists(fileName); } + + public readFile(fileName: string): string { + return this.shimHost.readFile(fileName); + } } function simpleForwardCall(logger: Logger, actionDescription: string, action: () => any, logPerformance: boolean): any { diff --git a/tests/baselines/reference/project/invalidRootFile/amd/invalidRootFile.errors.txt b/tests/baselines/reference/project/invalidRootFile/amd/invalidRootFile.errors.txt index 4433f671a1e..9cd0dd7b0cf 100644 --- a/tests/baselines/reference/project/invalidRootFile/amd/invalidRootFile.errors.txt +++ b/tests/baselines/reference/project/invalidRootFile/amd/invalidRootFile.errors.txt @@ -1,6 +1,6 @@ error TS6053: File 'a.ts' not found. -error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.d.ts', '.tsx'. +error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'. !!! error TS6053: File 'a.ts' not found. -!!! error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.d.ts', '.tsx'. \ No newline at end of file +!!! error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'. \ No newline at end of file diff --git a/tests/baselines/reference/project/invalidRootFile/node/invalidRootFile.errors.txt b/tests/baselines/reference/project/invalidRootFile/node/invalidRootFile.errors.txt index 4433f671a1e..9cd0dd7b0cf 100644 --- a/tests/baselines/reference/project/invalidRootFile/node/invalidRootFile.errors.txt +++ b/tests/baselines/reference/project/invalidRootFile/node/invalidRootFile.errors.txt @@ -1,6 +1,6 @@ error TS6053: File 'a.ts' not found. -error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.d.ts', '.tsx'. +error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'. !!! error TS6053: File 'a.ts' not found. -!!! error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.d.ts', '.tsx'. \ No newline at end of file +!!! error TS6054: File 'a.t' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts'. \ No newline at end of file diff --git a/tests/cases/unittests/reuseProgramStructure.ts b/tests/cases/unittests/reuseProgramStructure.ts index 5314ff48f55..6c043299c8f 100644 --- a/tests/cases/unittests/reuseProgramStructure.ts +++ b/tests/cases/unittests/reuseProgramStructure.ts @@ -104,10 +104,6 @@ module ts { files[t.name] = file; } - let moduleResolutionHost: ModuleResolutionHost = { - fileExists: fileName => hasProperty(files, fileName) - } - return { getSourceFile(fileName): SourceFile { return files[fileName]; @@ -130,7 +126,11 @@ module ts { getNewLine(): string { return sys.newLine; }, - getModuleResolutionHost: () => moduleResolutionHost + fileExists: fileName => hasProperty(files, fileName), + readFile: fileName => { + let file = lookUp(files, fileName); + return file && file.text; + } } }