diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5bb5142d570..37aadb74bec 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -535,9 +535,14 @@ namespace ts { let parseErrorBeforeNextFinishedNode = false; export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile { + // Using scriptKind as a condition handles both: + // - 'scriptKind' is unspecified and thus it is `undefined` + // - 'scriptKind' is set and it is `Unknown` (0) + // If the 'scriptKind' is 'undefined' or 'Unknown' then attempt + // to get the ScriptKind from the file name. + scriptKind = scriptKind ? scriptKind : getScriptKindFromFileName(fileName); - const isJavaScriptFile = scriptKind === ScriptKind.JS || hasJavaScriptFileExtension(fileName); - initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); + initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind); const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind); @@ -546,12 +551,28 @@ namespace ts { return result; } - function getLanguageVariant(fileName: string) { - // .tsx and .jsx files are treated as jsx language variant. - return fileExtensionIs(fileName, ".tsx") || fileExtensionIs(fileName, ".jsx") || fileExtensionIs(fileName, ".js") ? LanguageVariant.JSX : LanguageVariant.Standard; + function getScriptKindFromFileName(fileName: string): ScriptKind { + const ext = fileName.split(".").pop(); + switch (ext.toLowerCase()) { + case "js": + return ScriptKind.JS; + case "jsx": + return ScriptKind.JSX; + case "ts": + return ScriptKind.TS; + case "tsx": + return ScriptKind.TSX; + default: + return ScriptKind.TS; + } } - function initializeState(fileName: string, _sourceText: string, languageVersion: ScriptTarget, isJavaScriptFile: boolean, _syntaxCursor: IncrementalParser.SyntaxCursor) { + function getLanguageVariant(scriptKind: ScriptKind) { + // .tsx and .jsx files are treated as jsx language variant. + return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS ? LanguageVariant.JSX : LanguageVariant.Standard; + } + + function initializeState(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, scriptKind: ScriptKind) { NodeConstructor = objectAllocator.getNodeConstructor(); SourceFileConstructor = objectAllocator.getSourceFileConstructor(); @@ -564,14 +585,14 @@ namespace ts { identifierCount = 0; nodeCount = 0; - contextFlags = isJavaScriptFile ? NodeFlags.JavaScriptFile : NodeFlags.None; + contextFlags = scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX ? NodeFlags.JavaScriptFile : NodeFlags.None; parseErrorBeforeNextFinishedNode = false; // Initialize and prime the scanner before parsing the source elements. scanner.setText(sourceText); scanner.setOnError(scanError); scanner.setScriptTarget(languageVersion); - scanner.setLanguageVariant(getLanguageVariant(fileName)); + scanner.setLanguageVariant(getLanguageVariant(scriptKind)); } function clearState() { @@ -587,7 +608,7 @@ namespace ts { sourceText = undefined; } - function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind?: ScriptKind): SourceFile { + function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind: ScriptKind): SourceFile { sourceFile = createSourceFile(fileName, languageVersion, scriptKind); sourceFile.flags = contextFlags; @@ -655,7 +676,7 @@ namespace ts { } } - function createSourceFile(fileName: string, languageVersion: ScriptTarget, scriptKind?: ScriptKind): SourceFile { + function createSourceFile(fileName: string, languageVersion: ScriptTarget, scriptKind: ScriptKind): SourceFile { // code from createNode is inlined here so createNode won't have to deal with special case of creating source files // this is quite rare comparing to other nodes and createNode should be as fast as possible const sourceFile = new SourceFileConstructor(SyntaxKind.SourceFile, /*pos*/ 0, /* end */ sourceText.length); @@ -665,11 +686,9 @@ namespace ts { sourceFile.bindDiagnostics = []; sourceFile.languageVersion = languageVersion; sourceFile.fileName = normalizePath(fileName); - sourceFile.languageVariant = getLanguageVariant(sourceFile.fileName); + sourceFile.languageVariant = getLanguageVariant(scriptKind); sourceFile.isDeclarationFile = fileExtensionIs(sourceFile.fileName, ".d.ts"); - - scriptKind = scriptKind || ScriptKind.Unknown; - sourceFile.scriptKind = scriptKind !== ScriptKind.Unknown ? scriptKind : ScriptKind.TS; + sourceFile.scriptKind = scriptKind; return sourceFile; } @@ -5594,7 +5613,7 @@ namespace ts { } export function parseJSDocTypeExpressionForTests(content: string, start: number, length: number) { - initializeState("file.js", content, ScriptTarget.Latest, /*isJavaScriptFile*/ true, /*_syntaxCursor:*/ undefined); + initializeState("file.js", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); scanner.setText(content, start, length); token = scanner.scan(); const jsDocTypeExpression = parseJSDocTypeExpression(); @@ -5913,7 +5932,7 @@ namespace ts { } export function parseIsolatedJSDocComment(content: string, start: number, length: number) { - initializeState("file.js", content, ScriptTarget.Latest, /*isJavaScriptFile*/ true, /*_syntaxCursor:*/ undefined); + initializeState("file.js", content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS); sourceFile = { languageVariant: LanguageVariant.Standard, text: content }; const jsDocComment = parseJSDocCommentWorker(start, length); const diagnostics = parseDiagnostics; diff --git a/src/services/services.ts b/src/services/services.ts index cb5168039b9..0a7ef68eec7 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2857,6 +2857,12 @@ namespace ts { // 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 // host says should be used. + + // We do not support the scenario where a host can modify a registered + // file's script kind, i.e. in one project some file is treated as ".ts" + // and in another as ".js" + Debug.assert(hostFileInformation.scriptKind === oldSourceFile.scriptKind, "Registered script kind (" + oldSourceFile.scriptKind + ") should match new script kind (" + hostFileInformation.scriptKind +") for file: " + fileName); + return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); }