From 62bf4aefe23aeb67513af1b4961288244e9716a1 Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Thu, 25 Feb 2016 12:37:55 -0800 Subject: [PATCH 01/34] Updated binding for module.exports --- src/compiler/binder.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 933381afa92..1e24227a876 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -278,9 +278,11 @@ namespace ts { function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); + const isJsModuleExport = node.kind === SyntaxKind.BinaryExpression ? getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports : false; const isDefaultExport = node.flags & NodeFlags.Default; + // The exported symbol for an export default function/class node is always named "default" - const name = isDefaultExport && parent ? "default" : getDeclarationName(node); + const name = isJsModuleExport ? "export=" : isDefaultExport && parent ? "default" : getDeclarationName(node); let symbol: Symbol; if (name !== undefined) { @@ -1428,7 +1430,7 @@ namespace ts { function bindModuleExportsAssignment(node: BinaryExpression) { // 'module.exports = expr' assignment setCommonJsModuleIndicator(node); - bindExportAssignment(node); + declareSymbol(file.symbol.exports, file.symbol, node, SymbolFlags.Property | SymbolFlags.Export | SymbolFlags.ValueModule, SymbolFlags.None); } function bindThisPropertyAssignment(node: BinaryExpression) { From 9e46c180b4063e56eb947928615a3ebd46ecbe5e Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Thu, 25 Feb 2016 17:06:31 -0800 Subject: [PATCH 02/34] Fixed es2015 imports from export= --- src/compiler/checker.ts | 10 +++++++++- tests/cases/fourslash/javascriptModules20.ts | 13 ++++++++++++ tests/cases/fourslash/javascriptModules21.ts | 14 +++++++++++++ tests/cases/fourslash/javascriptModules22.ts | 13 ++++++++++++ tests/cases/fourslash/javascriptModules23.ts | 12 +++++++++++ tests/cases/fourslash/javascriptModules24.ts | 21 ++++++++++++++++++++ 6 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 tests/cases/fourslash/javascriptModules20.ts create mode 100644 tests/cases/fourslash/javascriptModules21.ts create mode 100644 tests/cases/fourslash/javascriptModules22.ts create mode 100644 tests/cases/fourslash/javascriptModules23.ts create mode 100644 tests/cases/fourslash/javascriptModules24.ts diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 603f5dae821..703777337af 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -960,8 +960,16 @@ namespace ts { if (targetSymbol) { const name = specifier.propertyName || specifier.name; if (name.text) { + let symbolFromVariable: Symbol; + // First check if module was specified with "export=". If so, get the member from the resolved type + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { + const members = (getTypeOfSymbol(targetSymbol) as ResolvedType).members; + symbolFromVariable = members && members[name.text]; + } + else { + symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text); + } const symbolFromModule = getExportOfModule(targetSymbol, name.text); - const symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text); const symbol = symbolFromModule && symbolFromVariable ? combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; diff --git a/tests/cases/fourslash/javascriptModules20.ts b/tests/cases/fourslash/javascriptModules20.ts new file mode 100644 index 00000000000..7ef5c73e1c3 --- /dev/null +++ b/tests/cases/fourslash/javascriptModules20.ts @@ -0,0 +1,13 @@ +/// +// @allowJs: true + +// @Filename: mod.js +//// function foo() { return {a: true}; } +//// module.exports = foo(); + +// @Filename: app.js +//// import * as mod from "./mod" +//// mod./**/ + +goTo.marker(); +verify.completionListContains('a'); diff --git a/tests/cases/fourslash/javascriptModules21.ts b/tests/cases/fourslash/javascriptModules21.ts new file mode 100644 index 00000000000..3a046515924 --- /dev/null +++ b/tests/cases/fourslash/javascriptModules21.ts @@ -0,0 +1,14 @@ +/// +// @allowJs: true +// @module: system + +// @Filename: mod.js +//// function foo() { return {a: true}; } +//// module.exports = foo(); + +// @Filename: app.js +//// import mod from "./mod" +//// mod./**/ + +goTo.marker(); +verify.completionListContains('a'); diff --git a/tests/cases/fourslash/javascriptModules22.ts b/tests/cases/fourslash/javascriptModules22.ts new file mode 100644 index 00000000000..67e3423ec40 --- /dev/null +++ b/tests/cases/fourslash/javascriptModules22.ts @@ -0,0 +1,13 @@ +/// +// @allowJs: true + +// @Filename: mod.js +//// function foo() { return {a: "hello, world"}; } +//// module.exports = foo(); + +// @Filename: app.js +//// import {a} from "./mod" +//// a./**/ + +goTo.marker(); +verify.completionListContains('toString'); diff --git a/tests/cases/fourslash/javascriptModules23.ts b/tests/cases/fourslash/javascriptModules23.ts new file mode 100644 index 00000000000..eafbea87baa --- /dev/null +++ b/tests/cases/fourslash/javascriptModules23.ts @@ -0,0 +1,12 @@ +/// + +// @Filename: mod.ts +//// var foo = {a: "test"}; +//// export = foo; + +// @Filename: app.ts +//// import {a} from "./mod" +//// a./**/ + +goTo.marker(); +verify.completionListContains('toString'); diff --git a/tests/cases/fourslash/javascriptModules24.ts b/tests/cases/fourslash/javascriptModules24.ts new file mode 100644 index 00000000000..5a0dd892db7 --- /dev/null +++ b/tests/cases/fourslash/javascriptModules24.ts @@ -0,0 +1,21 @@ +/// + +// @Filename: mod.ts +//// function foo() { return 42; } +//// namespace foo { +//// export function bar (a: string) { return a; } +//// } +//// export = foo; + +// @Filename: app.ts +//// import * as foo from "./mod" +//// foo/*1*/(); +//// foo.bar(/*2*/"test"); + +goTo.marker('1'); + +/**** BUG: Should be an error to invoke a call signature on a namespace import ****/ +//verify.errorExistsBeforeMarker('1'); +verify.quickInfoIs("(alias) foo(): number\nimport foo"); +goTo.marker('2'); +verify.signatureHelpArgumentCountIs(1); From c4a10cfcdd51f831c3039e305c1c465a85c93b0b Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Sun, 28 Feb 2016 18:25:04 -0800 Subject: [PATCH 03/34] Fixed default import from export equals --- src/compiler/binder.ts | 2 +- src/compiler/checker.ts | 9 ++++-- src/compiler/utilities.ts | 3 ++ .../reference/exportEqualsDefaultProperty.js | 27 ++++++++++++++++++ .../exportEqualsDefaultProperty.symbols | 21 ++++++++++++++ .../exportEqualsDefaultProperty.types | 28 +++++++++++++++++++ .../compiler/exportEqualsDefaultProperty.ts | 12 ++++++++ tests/cases/fourslash/javascriptModules22.ts | 19 +++++++++++++ 8 files changed, 117 insertions(+), 4 deletions(-) create mode 100644 tests/baselines/reference/exportEqualsDefaultProperty.js create mode 100644 tests/baselines/reference/exportEqualsDefaultProperty.symbols create mode 100644 tests/baselines/reference/exportEqualsDefaultProperty.types create mode 100644 tests/cases/compiler/exportEqualsDefaultProperty.ts diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index 1e24227a876..e0297145805 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -278,7 +278,7 @@ namespace ts { function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); - const isJsModuleExport = node.kind === SyntaxKind.BinaryExpression ? getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports : false; + const isJsModuleExport = getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports; const isDefaultExport = node.flags & NodeFlags.Default; // The exported symbol for an export default function/class node is always named "default" diff --git a/src/compiler/checker.ts b/src/compiler/checker.ts index 703777337af..ce01b5c8013 100644 --- a/src/compiler/checker.ts +++ b/src/compiler/checker.ts @@ -888,8 +888,12 @@ namespace ts { function getTargetOfImportClause(node: ImportClause): Symbol { const moduleSymbol = resolveExternalModuleName(node, (node.parent).moduleSpecifier); + if (moduleSymbol) { - const exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]); + const exportDefaultSymbol = moduleSymbol.exports["export="] ? + getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : + resolveSymbol(moduleSymbol.exports["default"]); + if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); } @@ -963,8 +967,7 @@ namespace ts { let symbolFromVariable: Symbol; // First check if module was specified with "export=". If so, get the member from the resolved type if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { - const members = (getTypeOfSymbol(targetSymbol) as ResolvedType).members; - symbolFromVariable = members && members[name.text]; + symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name.text); } else { symbolFromVariable = getPropertyOfVariable(targetSymbol, name.text); diff --git a/src/compiler/utilities.ts b/src/compiler/utilities.ts index fa9305ae722..42f439e0ded 100644 --- a/src/compiler/utilities.ts +++ b/src/compiler/utilities.ts @@ -1107,6 +1107,9 @@ namespace ts { /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property /// assignments we treat as special in the binder export function getSpecialPropertyAssignmentKind(expression: Node): SpecialPropertyAssignmentKind { + if (!isInJavaScriptFile(expression)) { + return SpecialPropertyAssignmentKind.None; + } if (expression.kind !== SyntaxKind.BinaryExpression) { return SpecialPropertyAssignmentKind.None; } diff --git a/tests/baselines/reference/exportEqualsDefaultProperty.js b/tests/baselines/reference/exportEqualsDefaultProperty.js new file mode 100644 index 00000000000..6d75ef5d95d --- /dev/null +++ b/tests/baselines/reference/exportEqualsDefaultProperty.js @@ -0,0 +1,27 @@ +//// [tests/cases/compiler/exportEqualsDefaultProperty.ts] //// + +//// [exp.ts] + +var x = { + "greeting": "hello, world", + "default": 42 +}; + +export = x + +//// [imp.ts] +import foo from "./exp"; +foo.toExponential(2); + + +//// [exp.js] +"use strict"; +var x = { + "greeting": "hello, world", + "default": 42 +}; +module.exports = x; +//// [imp.js] +"use strict"; +var exp_1 = require("./exp"); +exp_1["default"].toExponential(2); diff --git a/tests/baselines/reference/exportEqualsDefaultProperty.symbols b/tests/baselines/reference/exportEqualsDefaultProperty.symbols new file mode 100644 index 00000000000..54bbbde6956 --- /dev/null +++ b/tests/baselines/reference/exportEqualsDefaultProperty.symbols @@ -0,0 +1,21 @@ +=== tests/cases/compiler/exp.ts === + +var x = { +>x : Symbol(x, Decl(exp.ts, 1, 3)) + + "greeting": "hello, world", + "default": 42 +}; + +export = x +>x : Symbol(x, Decl(exp.ts, 1, 3)) + +=== tests/cases/compiler/imp.ts === +import foo from "./exp"; +>foo : Symbol(foo, Decl(imp.ts, 0, 6)) + +foo.toExponential(2); +>foo.toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --)) +>foo : Symbol(foo, Decl(imp.ts, 0, 6)) +>toExponential : Symbol(Number.toExponential, Decl(lib.d.ts, --, --)) + diff --git a/tests/baselines/reference/exportEqualsDefaultProperty.types b/tests/baselines/reference/exportEqualsDefaultProperty.types new file mode 100644 index 00000000000..31a6c8052f9 --- /dev/null +++ b/tests/baselines/reference/exportEqualsDefaultProperty.types @@ -0,0 +1,28 @@ +=== tests/cases/compiler/exp.ts === + +var x = { +>x : { "greeting": string; "default": number; } +>{ "greeting": "hello, world", "default": 42} : { "greeting": string; "default": number; } + + "greeting": "hello, world", +>"hello, world" : string + + "default": 42 +>42 : number + +}; + +export = x +>x : { "greeting": string; "default": number; } + +=== tests/cases/compiler/imp.ts === +import foo from "./exp"; +>foo : number + +foo.toExponential(2); +>foo.toExponential(2) : string +>foo.toExponential : (fractionDigits?: number) => string +>foo : number +>toExponential : (fractionDigits?: number) => string +>2 : number + diff --git a/tests/cases/compiler/exportEqualsDefaultProperty.ts b/tests/cases/compiler/exportEqualsDefaultProperty.ts new file mode 100644 index 00000000000..1adce3cabe1 --- /dev/null +++ b/tests/cases/compiler/exportEqualsDefaultProperty.ts @@ -0,0 +1,12 @@ + +// @Filename: exp.ts +var x = { + "greeting": "hello, world", + "default": 42 +}; + +export = x + +// @Filename: imp.ts +import foo from "./exp"; +foo.toExponential(2); diff --git a/tests/cases/fourslash/javascriptModules22.ts b/tests/cases/fourslash/javascriptModules22.ts index 67e3423ec40..89fa99b5ea2 100644 --- a/tests/cases/fourslash/javascriptModules22.ts +++ b/tests/cases/fourslash/javascriptModules22.ts @@ -5,9 +5,28 @@ //// function foo() { return {a: "hello, world"}; } //// module.exports = foo(); +// @Filename: mod2.js +//// var x = {name: 'test'}; +//// (function createExport(obj){ +//// module.exports = { +//// "default": x, +//// "sausages": {eggs: 2} +//// }; +//// })(); + // @Filename: app.js //// import {a} from "./mod" +//// import def, {sausages} from "./mod2" //// a./**/ goTo.marker(); verify.completionListContains('toString'); + +edit.backspace(2); +edit.insert("def."); +verify.completionListContains("name"); + +edit.insert("name;\nsausages."); +verify.completionListContains("eggs"); +edit.insert("eggs;"); +verify.numberOfErrorsInCurrentFile(0); From 83af10b05c297f78c7a5aeb2c95a6dc987c988ca Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 8 Mar 2016 13:43:02 -0800 Subject: [PATCH 04/34] Salsa: adding ScriptKind to ensure script blocks (.html, .aspx, .ascx, etc) are processed as JS --- src/compiler/parser.ts | 21 ++++++----- src/compiler/types.ts | 9 +++++ src/harness/harnessLanguageService.ts | 2 ++ src/server/editorServices.ts | 4 +++ src/services/services.ts | 52 +++++++++++++++------------ src/services/shims.ts | 9 +++++ 6 files changed, 66 insertions(+), 31 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5def394578d..7a8dbe99bc6 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -399,14 +399,15 @@ namespace ts { } } - export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false): SourceFile { + export function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes = false, scriptKind?: ScriptKind): SourceFile { const start = new Date().getTime(); - const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes); + const result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); parseTime += new Date().getTime() - start; return result; } + // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter // indicates what changed between the 'text' that this SourceFile has and the 'newText'. // The SourceFile will be created with the compiler attempting to reuse as many nodes from @@ -416,8 +417,8 @@ namespace ts { // from this SourceFile that are being held onto may change as a result (including // becoming detached from any SourceFile). It is recommended that this SourceFile not // be used once 'update' is called on it. - export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile { - return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); + export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean, scriptKind?: ScriptKind): SourceFile { + return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, scriptKind); } /* @internal */ @@ -533,8 +534,10 @@ namespace ts { // attached to the EOF token. let parseErrorBeforeNextFinishedNode = false; - export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean): SourceFile { - const isJavaScriptFile = hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0; + export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile { + + const isJavaScriptFile = hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0 || scriptKind == ScriptKind.Js; + initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes); @@ -6237,7 +6240,7 @@ namespace ts { } namespace IncrementalParser { - export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile { + export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean, scriptKind?: ScriptKind): SourceFile { aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive); checkChangeRange(sourceFile, newText, textChangeRange, aggressiveChecks); @@ -6249,7 +6252,7 @@ namespace ts { if (sourceFile.statements.length === 0) { // If we don't have any statements in the current source file, then there's no real // way to incrementally parse. So just do a full parse instead. - return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true); + return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, scriptKind); } // Make sure we're not trying to incrementally update a source file more than once. Once @@ -6313,7 +6316,7 @@ namespace ts { // inconsistent tree. Setting the parents on the new tree should be very fast. We // will immediately bail out of walking any subtrees when we can see that their parents // are already correct. - const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true); + const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, scriptKind); return result; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index d605a684a76..5f01c2a92b0 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1,3 +1,4 @@ + namespace ts { export interface Map { [index: string]: T; @@ -2476,6 +2477,14 @@ namespace ts { character: number; } + export const enum ScriptKind { + Unknown = 0, + Js = 1, + Jsx = 2, + Ts = 3, + Tsx = 4 + } + export const enum ScriptTarget { ES3 = 0, ES5 = 1, diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index a0cac439729..5887d77ab62 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -183,6 +183,7 @@ namespace Harness.LanguageService { const script = this.getScriptInfo(fileName); return script ? new ScriptSnapshot(script) : undefined; } + getScriptKind(fileName: string): ScriptKind { return ScriptKind.Unknown; } getScriptVersion(fileName: string): string { const script = this.getScriptInfo(fileName); return script ? script.version.toString() : undefined; @@ -253,6 +254,7 @@ namespace Harness.LanguageService { const nativeScriptSnapshot = this.nativeHost.getScriptSnapshot(fileName); return nativeScriptSnapshot && new ScriptSnapshotProxy(nativeScriptSnapshot); } + getScriptKind(fileName: string): ScriptKind { return this.nativeHost.getScriptKind(fileName); } getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); } getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); } diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index e2ec5cc159f..71907735b91 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -192,6 +192,10 @@ namespace ts.server { return this.roots.map(root => root.fileName); } + getScriptKind() { + return ScriptKind.Unknown; + } + getScriptVersion(filename: string) { return this.getScriptInfo(filename).svc.latestVersion().toString(); } diff --git a/src/services/services.ts b/src/services/services.ts index 0fb7f329dfe..5b4d0e77f42 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -818,8 +818,8 @@ namespace ts { super(kind, pos, end); } - public update(newText: string, textChangeRange: TextChangeRange): SourceFile { - return updateSourceFile(this, newText, textChangeRange); + public update(newText: string, textChangeRange: TextChangeRange, scriptKind?: ScriptKind): SourceFile { + return updateSourceFile(this, newText, textChangeRange, /*aggressiveChecks*/ undefined, scriptKind); } public getLineAndCharacterOfPosition(position: number): LineAndCharacter { @@ -1018,6 +1018,7 @@ namespace ts { getNewLine?(): string; getProjectVersion?(): string; getScriptFileNames(): string[]; + getScriptKind(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot; getLocalizedDiagnosticMessages?(): any; @@ -1467,7 +1468,8 @@ namespace ts { fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, - version: string): SourceFile; + version: string, + scriptKind?: ScriptKind): SourceFile; /** * Request an updated version of an already existing SourceFile with a given fileName @@ -1485,7 +1487,8 @@ namespace ts { fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, - version: string): SourceFile; + version: string, + scriptKind?: ScriptKind): SourceFile; /** * Informs the DocumentRegistry that a file is not needed any longer. @@ -1656,6 +1659,7 @@ namespace ts { hostFileName: string; version: string; scriptSnapshot: IScriptSnapshot; + scriptKind: ScriptKind; } interface DocumentRegistryEntry { @@ -1745,12 +1749,14 @@ namespace ts { private createEntry(fileName: string, path: Path) { let entry: HostFileInformation; + const scriptKind = this.host.getScriptKind(fileName); const scriptSnapshot = this.host.getScriptSnapshot(fileName); if (scriptSnapshot) { entry = { hostFileName: fileName, version: this.host.getScriptVersion(fileName), - scriptSnapshot: scriptSnapshot + scriptSnapshot: scriptSnapshot, + scriptKind: scriptKind }; } @@ -1816,17 +1822,18 @@ namespace ts { throw new Error("Could not find file: '" + fileName + "'."); } + const scriptKind = this.host.getScriptKind(fileName); const version = this.host.getScriptVersion(fileName); let sourceFile: SourceFile; if (this.currentFileName !== fileName) { // This is a new file, just parse it - sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, ScriptTarget.Latest, version, /*setNodeParents*/ true); + sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, ScriptTarget.Latest, version, /*setNodeParents*/ true, scriptKind); } else if (this.currentFileVersion !== version) { // This is the same file, just a newer version. Incrementally parse the file. const editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot); - sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange); + sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange, /*aggressiveChecks*/ undefined, scriptKind); } if (sourceFile) { @@ -1952,16 +1959,16 @@ namespace ts { return output.outputText; } - export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile { + export function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile { const text = scriptSnapshot.getText(0, scriptSnapshot.getLength()); - const sourceFile = createSourceFile(fileName, text, scriptTarget, setNodeParents); + const sourceFile = createSourceFile(fileName, text, scriptTarget, setNodeParents, scriptKind); setSourceFileFields(sourceFile, scriptSnapshot, version); return sourceFile; } export let disableIncrementalParsing = false; - export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile { + export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean, scriptKind?: ScriptKind): SourceFile { // If we were given a text change range, and our version or open-ness changed, then // incrementally parse this file. if (textChangeRange) { @@ -1995,7 +2002,7 @@ namespace ts { : (changedText + suffix); } - const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); + const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, scriptKind); setSourceFileFields(newSourceFile, scriptSnapshot, version); // after incremental parsing nameTable might not be up-to-date // drop it so it can be lazily recreated later @@ -2016,7 +2023,7 @@ namespace ts { } // Otherwise, just create a new source file. - return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true); + return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true, scriptKind); } export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry { @@ -2058,12 +2065,12 @@ namespace ts { return JSON.stringify(bucketInfoArray, undefined, 2); } - function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true); + function acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true, scriptKind); } - function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false); + function updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false, scriptKind); } function acquireOrUpdateDocument( @@ -2071,7 +2078,8 @@ namespace ts { compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, - acquiring: boolean): SourceFile { + acquiring: boolean, + scriptKind?: ScriptKind): SourceFile { const bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true); const path = toPath(fileName, currentDirectory, getCanonicalFileName); @@ -2080,7 +2088,7 @@ namespace ts { Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?"); // Have never seen this file with these settings. Create a new source file for it. - const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false); + const sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind); entry = { sourceFile: sourceFile, @@ -2094,8 +2102,8 @@ namespace ts { // the script snapshot. If so, update it appropriately. Otherwise, we can just // return it as is. if (entry.sourceFile.version !== version) { - entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, - scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot)); + entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, + scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot), /*aggressiveChecks*/ undefined, scriptKind); } } @@ -2844,14 +2852,14 @@ 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. - return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } // We didn't already have the file. Fall through and acquire it from the registry. } // Could not find this file in the old program, create a new SourceFile for it. - return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } function sourceFileUpToDate(sourceFile: SourceFile): boolean { diff --git a/src/services/shims.ts b/src/services/shims.ts index 9ca3f19244d..e29b15be4b1 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -55,6 +55,7 @@ namespace ts { /** Returns a JSON-encoded value of the type: string[] */ getScriptFileNames(): string; + getScriptKind(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): ScriptSnapshotShim; getLocalizedDiagnosticMessages(): string; @@ -344,6 +345,14 @@ namespace ts { return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot); } + public getScriptKind(fileName: string): ScriptKind { + try { + return this.shimHost.getScriptKind(fileName); + } catch (e) { + return ScriptKind.Unknown; + } + } + public getScriptVersion(fileName: string): string { return this.shimHost.getScriptVersion(fileName); } From 9b27c148e9b063758a24d21dc10b16210a7dc04c Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 16 Feb 2016 22:44:43 -0800 Subject: [PATCH 05/34] Fix to test harness and addressing CR comments --- src/compiler/parser.ts | 2 +- src/compiler/types.ts | 8 ++++---- src/harness/harnessLanguageService.ts | 4 ++-- src/services/services.ts | 6 +++--- src/services/shims.ts | 5 +++-- 5 files changed, 13 insertions(+), 12 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 7a8dbe99bc6..1b59de63e36 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -536,7 +536,7 @@ namespace ts { export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile { - const isJavaScriptFile = hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0 || scriptKind == ScriptKind.Js; + const isJavaScriptFile = hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0 || scriptKind == ScriptKind.JS; initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5f01c2a92b0..e76d4b01b54 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2479,10 +2479,10 @@ namespace ts { export const enum ScriptKind { Unknown = 0, - Js = 1, - Jsx = 2, - Ts = 3, - Tsx = 4 + JS = 1, + JSX = 2, + TS = 3, + TSX = 4 } export const enum ScriptTarget { diff --git a/src/harness/harnessLanguageService.ts b/src/harness/harnessLanguageService.ts index 5887d77ab62..cb8f85eea31 100644 --- a/src/harness/harnessLanguageService.ts +++ b/src/harness/harnessLanguageService.ts @@ -183,7 +183,7 @@ namespace Harness.LanguageService { const script = this.getScriptInfo(fileName); return script ? new ScriptSnapshot(script) : undefined; } - getScriptKind(fileName: string): ScriptKind { return ScriptKind.Unknown; } + getScriptKind(fileName: string): ts.ScriptKind { return ts.ScriptKind.Unknown; } getScriptVersion(fileName: string): string { const script = this.getScriptInfo(fileName); return script ? script.version.toString() : undefined; @@ -254,7 +254,7 @@ namespace Harness.LanguageService { const nativeScriptSnapshot = this.nativeHost.getScriptSnapshot(fileName); return nativeScriptSnapshot && new ScriptSnapshotProxy(nativeScriptSnapshot); } - getScriptKind(fileName: string): ScriptKind { return this.nativeHost.getScriptKind(fileName); } + getScriptKind(fileName: string): ts.ScriptKind { return this.nativeHost.getScriptKind(fileName); } getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); } getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); } diff --git a/src/services/services.ts b/src/services/services.ts index 5b4d0e77f42..0362c235ee9 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1018,7 +1018,7 @@ namespace ts { getNewLine?(): string; getProjectVersion?(): string; getScriptFileNames(): string[]; - getScriptKind(fileName: string): ScriptKind; + getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot; getLocalizedDiagnosticMessages?(): any; @@ -1749,7 +1749,7 @@ namespace ts { private createEntry(fileName: string, path: Path) { let entry: HostFileInformation; - const scriptKind = this.host.getScriptKind(fileName); + const scriptKind = this.host.getScriptKind ? this.host.getScriptKind(fileName) : ScriptKind.Unknown; const scriptSnapshot = this.host.getScriptSnapshot(fileName); if (scriptSnapshot) { entry = { @@ -1822,7 +1822,7 @@ namespace ts { throw new Error("Could not find file: '" + fileName + "'."); } - const scriptKind = this.host.getScriptKind(fileName); + const scriptKind = this.host.getScriptKind ? this.host.getScriptKind(fileName) : ScriptKind.Unknown; const version = this.host.getScriptVersion(fileName); let sourceFile: SourceFile; diff --git a/src/services/shims.ts b/src/services/shims.ts index e29b15be4b1..44d28aff286 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -55,7 +55,7 @@ namespace ts { /** Returns a JSON-encoded value of the type: string[] */ getScriptFileNames(): string; - getScriptKind(fileName: string): ScriptKind; + getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): ScriptSnapshotShim; getLocalizedDiagnosticMessages(): string; @@ -348,7 +348,8 @@ namespace ts { public getScriptKind(fileName: string): ScriptKind { try { return this.shimHost.getScriptKind(fileName); - } catch (e) { + } + catch (e) { return ScriptKind.Unknown; } } From cc02a805a9bf718ee8f4069e0d8109717170d394 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Wed, 17 Feb 2016 07:42:27 -0800 Subject: [PATCH 06/34] Removing trailing whitespace from services.ts --- src/services/services.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/services.ts b/src/services/services.ts index 0362c235ee9..fdc366e1574 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2102,7 +2102,7 @@ namespace ts { // the script snapshot. If so, update it appropriately. Otherwise, we can just // return it as is. if (entry.sourceFile.version !== version) { - entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, + entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot), /*aggressiveChecks*/ undefined, scriptKind); } } From f8353d4429c1aeb0f22d3ba91ad4f03164f76133 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 8 Mar 2016 13:48:41 -0800 Subject: [PATCH 07/34] Addressing CR comments - Adding ScriptKind to SourceFile - Only update ScriptKind on creation, on update use the SourceFile's scriptKind - If scriptKind is unknown, default to ScriptKind.Ts - Replacing try-catch with in this.shimHost --- src/compiler/parser.ts | 24 +++++++++++++----------- src/compiler/types.ts | 1 + src/services/services.ts | 15 ++++++++------- src/services/shims.ts | 4 ++-- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1b59de63e36..1d736fcf28d 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -417,8 +417,8 @@ namespace ts { // from this SourceFile that are being held onto may change as a result (including // becoming detached from any SourceFile). It is recommended that this SourceFile not // be used once 'update' is called on it. - export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean, scriptKind?: ScriptKind): SourceFile { - return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, scriptKind); + export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile { + return IncrementalParser.updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); } /* @internal */ @@ -536,11 +536,10 @@ namespace ts { export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile { - const isJavaScriptFile = hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0 || scriptKind == ScriptKind.JS; - + const isJavaScriptFile = scriptKind === ScriptKind.JS || hasJavaScriptFileExtension(fileName); initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); - const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes); + const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind); clearState(); @@ -588,8 +587,8 @@ namespace ts { sourceText = undefined; } - function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean): SourceFile { - sourceFile = createSourceFile(fileName, languageVersion); + function parseSourceFileWorker(fileName: string, languageVersion: ScriptTarget, setParentNodes: boolean, scriptKind?: ScriptKind): SourceFile { + sourceFile = createSourceFile(fileName, languageVersion, scriptKind); if (contextFlags & ParserContextFlags.JavaScriptFile) { sourceFile.parserContextFlags = ParserContextFlags.JavaScriptFile; @@ -659,7 +658,7 @@ namespace ts { } } - function createSourceFile(fileName: string, languageVersion: ScriptTarget): 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); @@ -672,6 +671,9 @@ namespace ts { sourceFile.flags = fileExtensionIs(sourceFile.fileName, ".d.ts") ? NodeFlags.DeclarationFile : 0; sourceFile.languageVariant = getLanguageVariant(sourceFile.fileName); + scriptKind = scriptKind || ScriptKind.Unknown; + sourceFile.scriptKind = scriptKind !== ScriptKind.Unknown ? scriptKind : ScriptKind.TS; + return sourceFile; } @@ -6240,7 +6242,7 @@ namespace ts { } namespace IncrementalParser { - export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean, scriptKind?: ScriptKind): SourceFile { + export function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks: boolean): SourceFile { aggressiveChecks = aggressiveChecks || Debug.shouldAssert(AssertionLevel.Aggressive); checkChangeRange(sourceFile, newText, textChangeRange, aggressiveChecks); @@ -6252,7 +6254,7 @@ namespace ts { if (sourceFile.statements.length === 0) { // If we don't have any statements in the current source file, then there's no real // way to incrementally parse. So just do a full parse instead. - return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, scriptKind); + return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, sourceFile.scriptKind); } // Make sure we're not trying to incrementally update a source file more than once. Once @@ -6316,7 +6318,7 @@ namespace ts { // inconsistent tree. Setting the parents on the new tree should be very fast. We // will immediately bail out of walking any subtrees when we can see that their parents // are already correct. - const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, scriptKind); + const result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind); return result; } diff --git a/src/compiler/types.ts b/src/compiler/types.ts index e76d4b01b54..666e20a6d29 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -1560,6 +1560,7 @@ namespace ts { hasNoDefaultLib: boolean; languageVersion: ScriptTarget; + /* @internal */ scriptKind: ScriptKind; // The first node that causes this file to be an external module /* @internal */ externalModuleIndicator: Node; diff --git a/src/services/services.ts b/src/services/services.ts index fdc366e1574..4f79ec14fdd 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -805,6 +805,7 @@ namespace ts { public identifierCount: number; public symbolCount: number; public version: string; + public scriptKind: ScriptKind; public languageVersion: ScriptTarget; public languageVariant: LanguageVariant; public identifiers: Map; @@ -818,8 +819,8 @@ namespace ts { super(kind, pos, end); } - public update(newText: string, textChangeRange: TextChangeRange, scriptKind?: ScriptKind): SourceFile { - return updateSourceFile(this, newText, textChangeRange, /*aggressiveChecks*/ undefined, scriptKind); + public update(newText: string, textChangeRange: TextChangeRange): SourceFile { + return updateSourceFile(this, newText, textChangeRange); } public getLineAndCharacterOfPosition(position: number): LineAndCharacter { @@ -1833,7 +1834,7 @@ namespace ts { else if (this.currentFileVersion !== version) { // This is the same file, just a newer version. Incrementally parse the file. const editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot); - sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange, /*aggressiveChecks*/ undefined, scriptKind); + sourceFile = updateLanguageServiceSourceFile(this.currentSourceFile, scriptSnapshot, version, editRange); } if (sourceFile) { @@ -1968,7 +1969,7 @@ namespace ts { export let disableIncrementalParsing = false; - export function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean, scriptKind?: ScriptKind): SourceFile { + 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 // incrementally parse this file. if (textChangeRange) { @@ -2002,7 +2003,7 @@ namespace ts { : (changedText + suffix); } - const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks, scriptKind); + const newSourceFile = updateSourceFile(sourceFile, newText, textChangeRange, aggressiveChecks); setSourceFileFields(newSourceFile, scriptSnapshot, version); // after incremental parsing nameTable might not be up-to-date // drop it so it can be lazily recreated later @@ -2023,7 +2024,7 @@ namespace ts { } // Otherwise, just create a new source file. - return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true, scriptKind); + return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true, sourceFile.scriptKind); } export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry { @@ -2103,7 +2104,7 @@ namespace ts { // return it as is. if (entry.sourceFile.version !== version) { entry.sourceFile = updateLanguageServiceSourceFile(entry.sourceFile, scriptSnapshot, version, - scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot), /*aggressiveChecks*/ undefined, scriptKind); + scriptSnapshot.getChangeRange(entry.sourceFile.scriptSnapshot)); } } diff --git a/src/services/shims.ts b/src/services/shims.ts index 44d28aff286..c1038815f2f 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -346,10 +346,10 @@ namespace ts { } public getScriptKind(fileName: string): ScriptKind { - try { + if ("getScriptKind" in this.shimHost) { return this.shimHost.getScriptKind(fileName); } - catch (e) { + else { return ScriptKind.Unknown; } } From 1c990bd242200557e131595d445ed81ed754a231 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 8 Mar 2016 14:35:01 -0800 Subject: [PATCH 08/34] Applying getScriptKindFromFileName to HostFileInformation before we set the ScriptKind. If the host returns ScriptKind 'Unknown' or 'undefined' we attempt to get the correct ScriptKind based on the file name. If this cannot be determined we return the default ScriptKind 'TS' --- src/compiler/parser.ts | 51 +++++++++++++++++++++++++++------------- src/services/services.ts | 6 +++++ 2 files changed, 41 insertions(+), 16 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 1d736fcf28d..e0bf1f4c2e9 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 ? ParserContextFlags.JavaScriptFile : ParserContextFlags.None; + contextFlags = scriptKind === ScriptKind.JS || scriptKind === ScriptKind.JSX ? ParserContextFlags.JavaScriptFile : ParserContextFlags.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); if (contextFlags & ParserContextFlags.JavaScriptFile) { @@ -658,7 +679,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); @@ -669,10 +690,8 @@ namespace ts { sourceFile.languageVersion = languageVersion; sourceFile.fileName = normalizePath(fileName); sourceFile.flags = fileExtensionIs(sourceFile.fileName, ".d.ts") ? NodeFlags.DeclarationFile : 0; - sourceFile.languageVariant = getLanguageVariant(sourceFile.fileName); - - scriptKind = scriptKind || ScriptKind.Unknown; - sourceFile.scriptKind = scriptKind !== ScriptKind.Unknown ? scriptKind : ScriptKind.TS; + sourceFile.languageVariant = getLanguageVariant(scriptKind); + sourceFile.scriptKind = scriptKind; return sourceFile; } @@ -5609,7 +5628,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(); @@ -5928,7 +5947,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 4f79ec14fdd..23db7b98a27 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -2853,6 +2853,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); } From da6059871faac02df3a0520bcaf96598b68ac906 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Thu, 18 Feb 2016 16:33:01 -0800 Subject: [PATCH 09/34] Applying getScriptKindFRomFileName to HostFileInformation before we set the ScriptKind. If the host returns ScriptKind 'Unknown' or 'undefined' we attempt to get the correct ScriptKind based on the file name. If this cannot be determined we return the default ScriptKind 'TS' --- src/compiler/parser.ts | 32 ++++++++++++++++---------------- src/services/services.ts | 4 ++-- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index e0bf1f4c2e9..5f9ea09fd6c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -407,6 +407,22 @@ namespace ts { return result; } + /* @internal */ + export 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; + } + } // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter // indicates what changed between the 'text' that this SourceFile has and the 'newText'. @@ -551,22 +567,6 @@ namespace ts { return result; } - 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 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; diff --git a/src/services/services.ts b/src/services/services.ts index 23db7b98a27..3dbd34cbedd 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -1757,7 +1757,7 @@ namespace ts { hostFileName: fileName, version: this.host.getScriptVersion(fileName), scriptSnapshot: scriptSnapshot, - scriptKind: scriptKind + scriptKind: scriptKind ? scriptKind : getScriptKindFromFileName(fileName) }; } @@ -2857,7 +2857,7 @@ namespace ts { // 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); + 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); } From bdc9788ec8a9de34255227f382879b8a98dd6f37 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Fri, 19 Feb 2016 14:20:32 -0800 Subject: [PATCH 10/34] Change to getScriptKindFromFileName to avoid unnecessary array allocation in split --- src/compiler/parser.ts | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 5f9ea09fd6c..17ed461d10f 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -409,15 +409,15 @@ namespace ts { /* @internal */ export function getScriptKindFromFileName(fileName: string): ScriptKind { - const ext = fileName.split(".").pop(); + const ext = fileName.substr(fileName.lastIndexOf(".")); switch (ext.toLowerCase()) { - case "js": + case ".js": return ScriptKind.JS; - case "jsx": + case ".jsx": return ScriptKind.JSX; - case "ts": + case ".ts": return ScriptKind.TS; - case "tsx": + case ".tsx": return ScriptKind.TSX; default: return ScriptKind.TS; From 8a29926d5c706516e9be4bf296dcd005b7389d2f Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 8 Mar 2016 14:42:42 -0800 Subject: [PATCH 11/34] Salsa: JS support for discovering and acquiring d.ts files (Mostly isolating VS host changes from PR#6448) --- Jakefile.js | 1 + src/compiler/commandLineParser.ts | 27 +++ src/compiler/core.ts | 26 +++ src/compiler/diagnosticMessages.json | 4 + src/compiler/parser.ts | 24 +-- src/compiler/types.ts | 8 + src/services/jsTyping.ts | 286 +++++++++++++++++++++++++++ src/services/services.ts | 6 +- src/services/shims.ts | 47 ++++- src/services/tsconfig.json | 1 + src/services/utilities.ts | 15 ++ 11 files changed, 416 insertions(+), 29 deletions(-) create mode 100644 src/services/jsTyping.ts diff --git a/Jakefile.js b/Jakefile.js index 7b85aacaa89..a0a001d7679 100644 --- a/Jakefile.js +++ b/Jakefile.js @@ -920,6 +920,7 @@ var servicesLintTargets = [ "patternMatcher.ts", "services.ts", "shims.ts", + "jsTyping.ts" ].map(function (s) { return path.join(servicesDirectory, s); }); diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 55cc2be0b72..e1e05c0a888 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -511,6 +511,7 @@ namespace ts { return { options, fileNames: getFileNames(), + typingOptions: getTypingOptions(), errors }; @@ -575,6 +576,32 @@ namespace ts { } return fileNames; } + + function getTypingOptions(): TypingOptions { + const options: TypingOptions = getBaseFileName(configFileName) === "jsconfig.json" + ? { enableAutoDiscovery: true, include: [], exclude: [] } + : { enableAutoDiscovery: false, include: [], exclude: [] }; + const jsonTypingOptions = json["typingOptions"]; + if (jsonTypingOptions) { + for (const id in jsonTypingOptions) { + if (id === "enableAutoDiscovery") { + if (typeof jsonTypingOptions[id] === "boolean") { + options.enableAutoDiscovery = jsonTypingOptions[id]; + } + } + else if (id === "include") { + options.include = isArray(jsonTypingOptions[id]) ? jsonTypingOptions[id] : []; + } + else if (id === "exclude") { + options.exclude = isArray(jsonTypingOptions[id]) ? jsonTypingOptions[id] : []; + } + else { + errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id)); + } + } + } + return options; + } } export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } { diff --git a/src/compiler/core.ts b/src/compiler/core.ts index cfdcb2b930c..b00753078fd 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -769,6 +769,32 @@ namespace ts { return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; } + export function ensureScriptKind(fileName: string, scriptKind?: ScriptKind): ScriptKind { + // 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 we attempt + // to get the ScriptKind from the file name. If it cannot be resolved + // from the file name then the default 'TS' script kind is returned. + return (scriptKind || getScriptKindFromFileName(fileName)) || ScriptKind.TS; + } + + export function getScriptKindFromFileName(fileName: string): ScriptKind { + const ext = fileName.substr(fileName.lastIndexOf(".")); + 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.Unknown; + } + } + /** * List of supported extensions in order of file resolution precedence. */ diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index 19112e88437..eb6c2bce814 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2654,5 +2654,9 @@ "'super' must be called before accessing 'this' in the constructor of a derived class.": { "category": "Error", "code": 17009 + }, + "Unknown typing option '{0}'.": { + "category": "Error", + "code": 17010 } } \ No newline at end of file diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts index 17ed461d10f..281adf9c90c 100644 --- a/src/compiler/parser.ts +++ b/src/compiler/parser.ts @@ -407,23 +407,6 @@ namespace ts { return result; } - /* @internal */ - export function getScriptKindFromFileName(fileName: string): ScriptKind { - const ext = fileName.substr(fileName.lastIndexOf(".")); - 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; - } - } - // Produces a new SourceFile for the 'newText' provided. The 'textChangeRange' parameter // indicates what changed between the 'text' that this SourceFile has and the 'newText'. // The SourceFile will be created with the compiler attempting to reuse as many nodes from @@ -551,12 +534,7 @@ 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); + scriptKind = ensureScriptKind(fileName, scriptKind); initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind); diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 666e20a6d29..946a68ca655 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2449,6 +2449,13 @@ namespace ts { [option: string]: string | number | boolean; } + export interface TypingOptions { + enableAutoDiscovery?: boolean; + include?: string[]; + exclude?: string[]; + [option: string]: any; + } + export const enum ModuleKind { None = 0, CommonJS = 1, @@ -2507,6 +2514,7 @@ namespace ts { export interface ParsedCommandLine { options: CompilerOptions; + typingOptions?: TypingOptions; fileNames: string[]; errors: Diagnostic[]; } diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts new file mode 100644 index 00000000000..bf3c6b6cc9a --- /dev/null +++ b/src/services/jsTyping.ts @@ -0,0 +1,286 @@ +// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. +// See LICENSE.txt in the project root for complete license information. + +/// + +/* @internal */ +namespace ts.JsTyping { + + interface TypingResolutionHost { + directoryExists: (path: string) => boolean; + fileExists: (fileName: string) => boolean; + readFile: (path: string, encoding?: string) => string; + readDirectory: (path: string, extension?: string, exclude?: string[], depth?: number) => string[]; + }; + + // A map of loose file names to library names + // that we are confident require typings + let safeList: Map; + const notFoundTypingNames: string[] = []; + + function tryParseJson(jsonPath: string, host: TypingResolutionHost): any { + if (host.fileExists(jsonPath)) { + try { + // Strip out single-line comments + const contents = host.readFile(jsonPath).replace(/^\/\/(.*)$/gm, ""); + return JSON.parse(contents); + } + catch (e) { } + } + return undefined; + } + + function isTypingEnabled(options: TypingOptions): boolean { + if (options) { + if (options.enableAutoDiscovery || + (options.include && options.include.length > 0) || + (options.exclude && options.exclude.length > 0)) { + return true; + } + } + return false; + } + + /** + * @param host is the object providing I/O related operations. + * @param fileNames are the file names that belong to the same project. + * @param globalCachePath is used to get the safe list file path and as cache path if the project root path isn't specified. + * @param projectRootPath is the path to the project root directory. This is used for the local typings cache. + * @param typingOptions are used for customizing the typing inference process. + * @param compilerOptions are used as a source of typing inference. + */ + export function discoverTypings( + host: TypingResolutionHost, + fileNames: string[], + globalCachePath: Path, + projectRootPath: Path, + typingOptions: TypingOptions, + compilerOptions: CompilerOptions) + : { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { + + // A typing name to typing file path mapping + const inferredTypings: Map = {}; + + if (!isTypingEnabled(typingOptions)) { + return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; + } + + const cachePath = projectRootPath ? projectRootPath : globalCachePath; + // Only infer typings for .js and .jsx files + fileNames = fileNames + .map(ts.normalizePath) + .filter(f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX)); + + const safeListFilePath = ts.combinePaths(globalCachePath, "safeList.json"); + if (!safeList && host.fileExists(safeListFilePath)) { + safeList = tryParseJson(safeListFilePath, host); + } + + const filesToWatch: string[] = []; + // Directories to search for package.json, bower.json and other typing information + let searchDirs: string[] = []; + let exclude: string[] = []; + + mergeTypings(typingOptions.include); + exclude = typingOptions.exclude ? typingOptions.exclude : []; + + if (typingOptions.enableAutoDiscovery) { + const possibleSearchDirs = fileNames.map(ts.getDirectoryPath); + if (projectRootPath !== undefined) { + possibleSearchDirs.push(projectRootPath); + } + searchDirs = ts.deduplicate(possibleSearchDirs); + for (const searchDir of searchDirs) { + const packageJsonPath = ts.combinePaths(searchDir, "package.json"); + getTypingNamesFromJson(packageJsonPath, filesToWatch); + + const bowerJsonPath = ts.combinePaths(searchDir, "bower.json"); + getTypingNamesFromJson(bowerJsonPath, filesToWatch); + + const nodeModulesPath = ts.combinePaths(searchDir, "node_modules"); + getTypingNamesFromNodeModuleFolder(nodeModulesPath, filesToWatch); + } + + getTypingNamesFromSourceFileNames(fileNames); + getTypingNamesFromCompilerOptions(compilerOptions); + } + + const typingsPath = ts.combinePaths(cachePath, "typings"); + const tsdJsonPath = ts.combinePaths(cachePath, "tsd.json"); + const tsdJsonDict = tryParseJson(tsdJsonPath, host); + if (tsdJsonDict) { + for (const notFoundTypingName of notFoundTypingNames) { + if (inferredTypings.hasOwnProperty(notFoundTypingName) && !inferredTypings[notFoundTypingName]) { + delete inferredTypings[notFoundTypingName]; + } + } + + // The "installed" property in the tsd.json serves as a registry of installed typings. Each item + // of this object has a key of the relative file path, and a value that contains the corresponding + // commit hash. + if (hasProperty(tsdJsonDict, "installed")) { + for (const cachedTypingPath in tsdJsonDict.installed) { + // Assuming the cachedTypingPath has the format of "[package name]/[file name]" + const cachedTypingName = cachedTypingPath.substr(0, cachedTypingPath.indexOf("/")); + // If the inferred[cachedTypingName] is already not null, which means we found a corresponding + // d.ts file that coming with the package. That one should take higher priority. + if (hasProperty(inferredTypings, cachedTypingName) && !inferredTypings[cachedTypingName]) { + inferredTypings[cachedTypingName] = ts.combinePaths(typingsPath, cachedTypingPath); + } + } + } + } + + // Remove typings that the user has added to the exclude list + for (const excludeTypingName of exclude) { + delete inferredTypings[excludeTypingName]; + } + + const newTypingNames: string[] = []; + const cachedTypingPaths: string[] = []; + for (const typing in inferredTypings) { + if (inferredTypings[typing] !== undefined) { + cachedTypingPaths.push(inferredTypings[typing]); + } + else { + newTypingNames.push(typing); + } + } + return { cachedTypingPaths, newTypingNames, filesToWatch }; + + /** + * Merge a given list of typingNames to the inferredTypings map + */ + function mergeTypings(typingNames: string[]) { + if (!typingNames) { + return; + } + + for (const typing of typingNames) { + if (!inferredTypings.hasOwnProperty(typing)) { + inferredTypings[typing] = undefined; + } + } + } + + /** + * Get the typing info from common package manager json files like package.json or bower.json + */ + function getTypingNamesFromJson(jsonPath: string, filesToWatch: string[]) { + const jsonDict = tryParseJson(jsonPath, host); + if (jsonDict) { + filesToWatch.push(jsonPath); + if (jsonDict.hasOwnProperty("dependencies")) { + mergeTypings(Object.keys(jsonDict.dependencies)); + } + } + } + + /** + * Infer typing names from given file names. For example, the file name "jquery-min.2.3.4.js" + * should be inferred to the 'jquery' typing name; and "angular-route.1.2.3.js" should be inferred + * to the 'angular-route' typing name. + * @param fileNames are the names for source files in the project + */ + function getTypingNamesFromSourceFileNames(fileNames: string[]) { + const jsFileNames = fileNames.filter(hasJavaScriptFileExtension); + const inferredTypingNames = jsFileNames.map(f => ts.removeFileExtension(ts.getBaseFileName(f.toLowerCase()))); + const cleanedTypingNames = inferredTypingNames.map(f => f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, "")); + safeList === undefined ? mergeTypings(cleanedTypingNames) : mergeTypings(cleanedTypingNames.filter(f => safeList.hasOwnProperty(f))); + + const jsxFileNames = fileNames.filter(f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX)); + if (jsxFileNames.length > 0) { + mergeTypings(["react"]); + } + } + + /** + * Infer typing names from node_module folder + * @param nodeModulesPath is the path to the "node_modules" folder + */ + function getTypingNamesFromNodeModuleFolder(nodeModulesPath: string, filesToWatch: string[]) { + // Todo: add support for ModuleResolutionHost too + if (!host.directoryExists(nodeModulesPath)) { + return; + } + + const typingNames: string[] = []; + const packageJsonFiles = + host.readDirectory(nodeModulesPath, /*extension*/ undefined, /*exclude*/ undefined, /*depth*/ 2).filter(f => ts.getBaseFileName(f) === "package.json"); + for (const packageJsonFile of packageJsonFiles) { + const packageJsonDict = tryParseJson(packageJsonFile, host); + if (!packageJsonDict) { continue; } + + filesToWatch.push(packageJsonFile); + + // npm 3 has the package.json contains a "_requiredBy" field + // we should include all the top level module names for npm 2, and only module names whose + // "_requiredBy" field starts with "#" or equals "/" for npm 3. + if (packageJsonDict._requiredBy && + packageJsonDict._requiredBy.filter((r: string) => r[0] === "#" || r === "/").length === 0) { + continue; + } + + // If the package has its own d.ts typings, those will take precedence. Otherwise the package name will be used + // to download d.ts files from DefinitelyTyped + const packageName = packageJsonDict["name"]; + if (packageJsonDict.hasOwnProperty("typings")) { + const absPath = ts.getNormalizedAbsolutePath(packageJsonDict.typings, ts.getDirectoryPath(packageJsonFile)); + inferredTypings[packageName] = absPath; + } + else { + typingNames.push(packageName); + } + } + mergeTypings(typingNames); + } + + function getTypingNamesFromCompilerOptions(options: CompilerOptions) { + const typingNames: string[] = []; + if (!options) { + return; + } + + if (options.jsx === JsxEmit.React) { + typingNames.push("react"); + } + if (options.moduleResolution === ModuleResolutionKind.NodeJs) { + typingNames.push("node"); + } + mergeTypings(typingNames); + } + } + + /** + * Keep a list of typings names that we know cannot be obtained at the moment (could be because + * of network issues or because the package doesn't hava a d.ts file in DefinitelyTyped), so + * that we won't try again next time within this session. + * @param newTypingNames The list of new typings that the host attempted to acquire + * @param cachePath The path to the tsd.json cache + * @param host The object providing I/O related operations. + */ + export function updateNotFoundTypingNames(newTypingNames: string[], cachePath: string, host: TypingResolutionHost): void { + const tsdJsonPath = ts.combinePaths(cachePath, "tsd.json"); + const cacheTsdJsonDict = tryParseJson(tsdJsonPath, host); + if (cacheTsdJsonDict) { + const installedTypingFiles = hasProperty(cacheTsdJsonDict, "installed") + ? Object.keys(cacheTsdJsonDict.installed) + : []; + const newMissingTypingNames = + ts.filter(newTypingNames, name => notFoundTypingNames.indexOf(name) < 0 && !isInstalled(name, installedTypingFiles)); + for (const newMissingTypingName of newMissingTypingNames) { + notFoundTypingNames.push(newMissingTypingName); + } + } + } + + function isInstalled(typing: string, installedKeys: string[]) { + const typingPrefix = typing + "/"; + for (const key of installedKeys) { + if (key.indexOf(typingPrefix) === 0) { + return true; + } + } + return false; + } +} diff --git a/src/services/services.ts b/src/services/services.ts index 3dbd34cbedd..4f7e2ba8d1e 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -7,6 +7,7 @@ /// /// /// +/// /// /// @@ -1750,14 +1751,13 @@ namespace ts { private createEntry(fileName: string, path: Path) { let entry: HostFileInformation; - const scriptKind = this.host.getScriptKind ? this.host.getScriptKind(fileName) : ScriptKind.Unknown; const scriptSnapshot = this.host.getScriptSnapshot(fileName); if (scriptSnapshot) { entry = { hostFileName: fileName, version: this.host.getScriptVersion(fileName), scriptSnapshot: scriptSnapshot, - scriptKind: scriptKind ? scriptKind : getScriptKindFromFileName(fileName) + scriptKind: getScriptKind(fileName, this.host) }; } @@ -1823,7 +1823,7 @@ namespace ts { throw new Error("Could not find file: '" + fileName + "'."); } - const scriptKind = this.host.getScriptKind ? this.host.getScriptKind(fileName) : ScriptKind.Unknown; + const scriptKind = getScriptKind(fileName, this.host); const version = this.host.getScriptVersion(fileName); let sourceFile: SourceFile; diff --git a/src/services/shims.ts b/src/services/shims.ts index c1038815f2f..350506924fb 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -78,7 +78,7 @@ namespace ts { * @param exclude A JSON encoded string[] containing the paths to exclude * when enumerating the directory. */ - readDirectory(rootDir: string, extension: string, exclude?: string): string; + readDirectory(rootDir: string, extension: string, exclude?: string, depth?: number): string; } /// @@ -230,6 +230,8 @@ namespace ts { getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getDefaultCompilationSettings(): string; + resolveTypeDefinitions(fileNamesJson: string, globalCachePath: string, projectRootPath: string, typingOptionsJson: string, compilerOptionsJson: string): string; + updateNotFoundTypingNames(newTypingsJson: string, globalCachePath: string, projectRootPath: string): string; } function logInternalError(logger: Logger, err: Error) { @@ -420,8 +422,16 @@ namespace ts { } } - public readDirectory(rootDir: string, extension: string, exclude: string[]): string[] { - const encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)); + public readDirectory(rootDir: string, extension: string, exclude: string[], depth?: number): string[] { + // Wrap the API changes for 2.0 release. This try/catch + // should be removed once TypeScript 2.0 has shipped. + let encoded: string; + try { + encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude), depth); + } + catch (e) { + encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)); + } return JSON.parse(encoded); } @@ -951,6 +961,7 @@ namespace ts { if (result.error) { return { options: {}, + typingOptions: {}, files: [], errors: [realizeDiagnostic(result.error, "\r\n")] }; @@ -961,6 +972,7 @@ namespace ts { return { options: configFile.options, + typingOptions: configFile.typingOptions, files: configFile.fileNames, errors: realizeDiagnostics(configFile.errors, "\r\n") }; @@ -973,6 +985,35 @@ namespace ts { () => getDefaultCompilerOptions() ); } + + public resolveTypeDefinitions(fileNamesJson: string, globalCachePath: string, projectRootPath: string, typingOptionsJson: string, compilerOptionsJson: string): string { + const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false); + return this.forwardJSONCall("resolveTypeDefinitions()", () => { + const cachePath = projectRootPath ? projectRootPath : globalCachePath; + const typingOptions = JSON.parse(typingOptionsJson); + // Convert the include and exclude lists from a semi-colon delimited string to a string array + typingOptions.include = typingOptions.include ? typingOptions.include.toString().split(";") : []; + typingOptions.exclude = typingOptions.exclude ? typingOptions.exclude.toString().split(";") : []; + + const compilerOptions = JSON.parse(compilerOptionsJson); + const fileNames: string[] = JSON.parse(fileNamesJson); + return ts.JsTyping.discoverTypings( + this.host, + fileNames, + toPath(globalCachePath, globalCachePath, getCanonicalFileName), + toPath(cachePath, cachePath, getCanonicalFileName), + typingOptions, + compilerOptions); + }); + } + + public updateNotFoundTypingNames(newTypingsJson: string, globalCachePath: string, projectRootPath: string): string { + return this.forwardJSONCall("updateNotFoundTypingNames()", () => { + const newTypingNames: string[] = JSON.parse(newTypingsJson); + const cachePath = projectRootPath ? projectRootPath : globalCachePath; + ts.JsTyping.updateNotFoundTypingNames(newTypingNames, cachePath, this.host); + }); + } } export class TypeScriptServicesFactory implements ShimFactory { diff --git a/src/services/tsconfig.json b/src/services/tsconfig.json index 5dcee789243..43c4477115b 100644 --- a/src/services/tsconfig.json +++ b/src/services/tsconfig.json @@ -30,6 +30,7 @@ "shims.ts", "signatureHelp.ts", "utilities.ts", + "jsTyping.ts", "formatting/formatting.ts", "formatting/formattingContext.ts", "formatting/formattingRequestKind.ts", diff --git a/src/services/utilities.ts b/src/services/utilities.ts index afdc85fffd8..e423d870ca4 100644 --- a/src/services/utilities.ts +++ b/src/services/utilities.ts @@ -837,4 +837,19 @@ namespace ts { }; return name; } + + export function scriptKindIs(fileName: string, host: LanguageServiceHost, ...scriptKinds: ScriptKind[]): boolean { + const scriptKind = getScriptKind(fileName, host); + return forEach(scriptKinds, k => k === scriptKind); + } + + export function getScriptKind(fileName: string, host?: LanguageServiceHost): ScriptKind { + // First check to see if the script kind can be determined from the file name + var scriptKind = getScriptKindFromFileName(fileName); + if (scriptKind === ScriptKind.Unknown && host && host.getScriptKind) { + // Next check to see if the host can resolve the script kind + scriptKind = host.getScriptKind(fileName); + } + return ensureScriptKind(fileName, scriptKind); + } } \ No newline at end of file From 2f90271ca67ef5ee73e5469dbae3e36b0903262d Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Sun, 21 Feb 2016 21:57:37 -0800 Subject: [PATCH 12/34] Fixing lint issues caught by Travis CI build (Rules appear to be more strict - this was not caught on a local lint run) --- src/services/jsTyping.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index bf3c6b6cc9a..e4b221bf051 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -55,8 +55,8 @@ namespace ts.JsTyping { globalCachePath: Path, projectRootPath: Path, typingOptions: TypingOptions, - compilerOptions: CompilerOptions) - : { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { + compilerOptions: CompilerOptions): + { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { // A typing name to typing file path mapping const inferredTypings: Map = {}; From cc3d4d3291e2fc3c52ee960d2b423ed2c4d06ba8 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 8 Mar 2016 15:17:09 -0800 Subject: [PATCH 13/34] Addressing CR comments - Adding check to ensure TypingOptions 'include' and 'exclude' arrays are composed of strings - Allow leading whitespace when removing comments from json --- src/compiler/commandLineParser.ts | 32 +++++++++++++++++++++++++++---- src/services/jsTyping.ts | 6 +++--- 2 files changed, 31 insertions(+), 7 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index e1e05c0a888..7d365c6b283 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -590,10 +590,10 @@ namespace ts { } } else if (id === "include") { - options.include = isArray(jsonTypingOptions[id]) ? jsonTypingOptions[id] : []; + options.include = ConvertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); } else if (id === "exclude") { - options.exclude = isArray(jsonTypingOptions[id]) ? jsonTypingOptions[id] : []; + options.exclude = ConvertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); } else { errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id)); @@ -636,8 +636,8 @@ namespace ts { } } if (opt.isFilePath) { - value = normalizePath(combinePaths(basePath, value)); - if (value === "") { + value = normalizePath(combinePaths(basePath, value)); + if (value === "") { value = "."; } } @@ -654,4 +654,28 @@ namespace ts { return { options, errors }; } + + function ConvertJsonOptionToStringArray(optionName: string, optionJson: any, errors: Diagnostic[], func?: (element: string) => string): string[] { + let items: string[] = []; + let invalidOptionType = false; + if (!isArray(optionJson)) { + invalidOptionType = true; + } + else { + for (const element of optionJson) { + if (typeof element === "string") { + const item = func ? func(element) : element; + items.push(item); + } + else { + invalidOptionType = true; + break; + } + } + } + if (invalidOptionType) { + errors.push(createCompilerDiagnostic(Diagnostics.Option_0_should_have_array_of_strings_as_a_value, optionName)); + } + return items; + } } diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index e4b221bf051..bfb62e396c6 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -22,7 +22,7 @@ namespace ts.JsTyping { if (host.fileExists(jsonPath)) { try { // Strip out single-line comments - const contents = host.readFile(jsonPath).replace(/^\/\/(.*)$/gm, ""); + const contents = host.readFile(jsonPath).replace(/^\s*\/\/(.*)$/gm, ""); return JSON.parse(contents); } catch (e) { } @@ -65,7 +65,7 @@ namespace ts.JsTyping { return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; } - const cachePath = projectRootPath ? projectRootPath : globalCachePath; + const cachePath = projectRootPath || globalCachePath; // Only infer typings for .js and .jsx files fileNames = fileNames .map(ts.normalizePath) @@ -82,7 +82,7 @@ namespace ts.JsTyping { let exclude: string[] = []; mergeTypings(typingOptions.include); - exclude = typingOptions.exclude ? typingOptions.exclude : []; + exclude = typingOptions.exclude || []; if (typingOptions.enableAutoDiscovery) { const possibleSearchDirs = fileNames.map(ts.getDirectoryPath); From 9f2e36e59d6cf5655c586833e98b56d30b0a03f9 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Mon, 22 Feb 2016 19:33:45 -0800 Subject: [PATCH 14/34] Switch let -> const from lint validation --- src/compiler/commandLineParser.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 7d365c6b283..6d6392ff848 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -656,7 +656,7 @@ namespace ts { } function ConvertJsonOptionToStringArray(optionName: string, optionJson: any, errors: Diagnostic[], func?: (element: string) => string): string[] { - let items: string[] = []; + const items: string[] = []; let invalidOptionType = false; if (!isArray(optionJson)) { invalidOptionType = true; From 5cdb97cbfd2d6cdf5f31860a8baab02be8985a1c Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 23 Feb 2016 10:31:56 -0800 Subject: [PATCH 15/34] Adding devDependencies to the list of typings to merge --- src/services/jsTyping.ts | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index bfb62e396c6..d561734661b 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -173,6 +173,9 @@ namespace ts.JsTyping { if (jsonDict.hasOwnProperty("dependencies")) { mergeTypings(Object.keys(jsonDict.dependencies)); } + if (jsonDict.hasOwnProperty("devDependencies")) { + mergeTypings(Object.keys(jsonDict.devDependencies)); + } } } From 0de8f9eeb2b1b90bfc3a38c6ab5f1eac0f7acec8 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 23 Feb 2016 13:30:24 -0800 Subject: [PATCH 16/34] Using removeComments from commandLineParser. This is more robust as it removes both single and multiline comments --- src/compiler/commandLineParser.ts | 2 +- src/services/jsTyping.ts | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 6d6392ff848..11b9576c87a 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -477,7 +477,7 @@ namespace ts { * * This method replace comment content by whitespace rather than completely remove them to keep positions in json parsing error reporting accurate. */ - function removeComments(jsonText: string): string { + export function removeComments(jsonText: string): string { let output = ""; const scanner = createScanner(ScriptTarget.ES5, /* skipTrivia */ false, LanguageVariant.Standard, jsonText); let token: SyntaxKind; diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index d561734661b..251995a2991 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -21,8 +21,7 @@ namespace ts.JsTyping { function tryParseJson(jsonPath: string, host: TypingResolutionHost): any { if (host.fileExists(jsonPath)) { try { - // Strip out single-line comments - const contents = host.readFile(jsonPath).replace(/^\s*\/\/(.*)$/gm, ""); + const contents = removeComments(host.readFile(jsonPath)); return JSON.parse(contents); } catch (e) { } From 8b9e3515256a98b89cd5efdf7e378ae3d6002b99 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Thu, 25 Feb 2016 12:32:43 -0800 Subject: [PATCH 17/34] - renaming resolveTypeDefinitions to discoverTypings for consistency with jsTypings - simplifying typingOptions parsing after associated managed host changes --- src/services/shims.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/services/shims.ts b/src/services/shims.ts index 350506924fb..19512a49b8e 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -230,7 +230,7 @@ namespace ts { getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getDefaultCompilationSettings(): string; - resolveTypeDefinitions(fileNamesJson: string, globalCachePath: string, projectRootPath: string, typingOptionsJson: string, compilerOptionsJson: string): string; + discoverTypings(fileNamesJson: string, globalCachePath: string, projectRootPath: string, typingOptionsJson: string, compilerOptionsJson: string): string; updateNotFoundTypingNames(newTypingsJson: string, globalCachePath: string, projectRootPath: string): string; } @@ -986,14 +986,11 @@ namespace ts { ); } - public resolveTypeDefinitions(fileNamesJson: string, globalCachePath: string, projectRootPath: string, typingOptionsJson: string, compilerOptionsJson: string): string { + public discoverTypings(fileNamesJson: string, globalCachePath: string, projectRootPath: string, typingOptionsJson: string, compilerOptionsJson: string): string { const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false); - return this.forwardJSONCall("resolveTypeDefinitions()", () => { + return this.forwardJSONCall("discoverTypings()", () => { const cachePath = projectRootPath ? projectRootPath : globalCachePath; const typingOptions = JSON.parse(typingOptionsJson); - // Convert the include and exclude lists from a semi-colon delimited string to a string array - typingOptions.include = typingOptions.include ? typingOptions.include.toString().split(";") : []; - typingOptions.exclude = typingOptions.exclude ? typingOptions.exclude.toString().split(";") : []; const compilerOptions = JSON.parse(compilerOptionsJson); const fileNames: string[] = JSON.parse(fileNamesJson); From 830a10fc92864ec78f3044adc206222aea7c47dd Mon Sep 17 00:00:00 2001 From: zhengbli Date: Fri, 26 Feb 2016 14:15:07 -0800 Subject: [PATCH 18/34] Unify the use of "filter", "map" and "Object.keys" functions --- src/compiler/core.ts | 8 +++++++ src/services/jsTyping.ts | 45 ++++++++++++++++++++++------------------ 2 files changed, 33 insertions(+), 20 deletions(-) diff --git a/src/compiler/core.ts b/src/compiler/core.ts index b00753078fd..b3c7657b872 100644 --- a/src/compiler/core.ts +++ b/src/compiler/core.ts @@ -280,6 +280,14 @@ namespace ts { return hasOwnProperty.call(map, key); } + export function getKeys(map: Map): string[] { + const keys: string[] = []; + for (const key in map) { + keys.push(key); + } + return keys; + } + export function getProperty(map: Map, key: string): T { return hasOwnProperty.call(map, key) ? map[key] : undefined; } diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 251995a2991..b3955e89148 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -66,9 +66,7 @@ namespace ts.JsTyping { const cachePath = projectRootPath || globalCachePath; // Only infer typings for .js and .jsx files - fileNames = fileNames - .map(ts.normalizePath) - .filter(f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX)); + fileNames = filter(map(fileNames, ts.normalizePath), f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX)); const safeListFilePath = ts.combinePaths(globalCachePath, "safeList.json"); if (!safeList && host.fileExists(safeListFilePath)) { @@ -84,7 +82,7 @@ namespace ts.JsTyping { exclude = typingOptions.exclude || []; if (typingOptions.enableAutoDiscovery) { - const possibleSearchDirs = fileNames.map(ts.getDirectoryPath); + const possibleSearchDirs = map(fileNames, ts.getDirectoryPath); if (projectRootPath !== undefined) { possibleSearchDirs.push(projectRootPath); } @@ -109,7 +107,7 @@ namespace ts.JsTyping { const tsdJsonDict = tryParseJson(tsdJsonPath, host); if (tsdJsonDict) { for (const notFoundTypingName of notFoundTypingNames) { - if (inferredTypings.hasOwnProperty(notFoundTypingName) && !inferredTypings[notFoundTypingName]) { + if (hasProperty(inferredTypings, notFoundTypingName) && !inferredTypings[notFoundTypingName]) { delete inferredTypings[notFoundTypingName]; } } @@ -156,7 +154,7 @@ namespace ts.JsTyping { } for (const typing of typingNames) { - if (!inferredTypings.hasOwnProperty(typing)) { + if (!hasProperty(inferredTypings, typing)) { inferredTypings[typing] = undefined; } } @@ -169,11 +167,11 @@ namespace ts.JsTyping { const jsonDict = tryParseJson(jsonPath, host); if (jsonDict) { filesToWatch.push(jsonPath); - if (jsonDict.hasOwnProperty("dependencies")) { - mergeTypings(Object.keys(jsonDict.dependencies)); + if (hasProperty(jsonDict, "dependencies")) { + mergeTypings(getKeys(jsonDict.dependencies)); } - if (jsonDict.hasOwnProperty("devDependencies")) { - mergeTypings(Object.keys(jsonDict.devDependencies)); + if (hasProperty(jsonDict, "devDependencies")) { + mergeTypings(getKeys(jsonDict.devDependencies)); } } } @@ -185,12 +183,17 @@ namespace ts.JsTyping { * @param fileNames are the names for source files in the project */ function getTypingNamesFromSourceFileNames(fileNames: string[]) { - const jsFileNames = fileNames.filter(hasJavaScriptFileExtension); - const inferredTypingNames = jsFileNames.map(f => ts.removeFileExtension(ts.getBaseFileName(f.toLowerCase()))); - const cleanedTypingNames = inferredTypingNames.map(f => f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, "")); - safeList === undefined ? mergeTypings(cleanedTypingNames) : mergeTypings(cleanedTypingNames.filter(f => safeList.hasOwnProperty(f))); + const jsFileNames = filter(fileNames, hasJavaScriptFileExtension); + const inferredTypingNames = map(jsFileNames, f => ts.removeFileExtension(ts.getBaseFileName(f.toLowerCase()))); + const cleanedTypingNames = map(inferredTypingNames, f => f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, "")); + if (safeList === undefined) { + mergeTypings(cleanedTypingNames); + } + else { + mergeTypings(filter(cleanedTypingNames, f => hasProperty(safeList, f))); + } - const jsxFileNames = fileNames.filter(f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX)); + const jsxFileNames = filter(fileNames, f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX)); if (jsxFileNames.length > 0) { mergeTypings(["react"]); } @@ -208,7 +211,9 @@ namespace ts.JsTyping { const typingNames: string[] = []; const packageJsonFiles = - host.readDirectory(nodeModulesPath, /*extension*/ undefined, /*exclude*/ undefined, /*depth*/ 2).filter(f => ts.getBaseFileName(f) === "package.json"); + filter( + host.readDirectory(nodeModulesPath, /*extension*/ undefined, /*exclude*/ undefined, /*depth*/ 2), + f => ts.getBaseFileName(f) === "package.json"); for (const packageJsonFile of packageJsonFiles) { const packageJsonDict = tryParseJson(packageJsonFile, host); if (!packageJsonDict) { continue; } @@ -219,14 +224,14 @@ namespace ts.JsTyping { // we should include all the top level module names for npm 2, and only module names whose // "_requiredBy" field starts with "#" or equals "/" for npm 3. if (packageJsonDict._requiredBy && - packageJsonDict._requiredBy.filter((r: string) => r[0] === "#" || r === "/").length === 0) { + filter(packageJsonDict._requiredBy, (r: string) => r[0] === "#" || r === "/").length === 0) { continue; } // If the package has its own d.ts typings, those will take precedence. Otherwise the package name will be used // to download d.ts files from DefinitelyTyped const packageName = packageJsonDict["name"]; - if (packageJsonDict.hasOwnProperty("typings")) { + if (hasProperty(packageJsonDict, "typings")) { const absPath = ts.getNormalizedAbsolutePath(packageJsonDict.typings, ts.getDirectoryPath(packageJsonFile)); inferredTypings[packageName] = absPath; } @@ -266,10 +271,10 @@ namespace ts.JsTyping { const cacheTsdJsonDict = tryParseJson(tsdJsonPath, host); if (cacheTsdJsonDict) { const installedTypingFiles = hasProperty(cacheTsdJsonDict, "installed") - ? Object.keys(cacheTsdJsonDict.installed) + ? getKeys(cacheTsdJsonDict.installed) : []; const newMissingTypingNames = - ts.filter(newTypingNames, name => notFoundTypingNames.indexOf(name) < 0 && !isInstalled(name, installedTypingFiles)); + filter(newTypingNames, name => notFoundTypingNames.indexOf(name) < 0 && !isInstalled(name, installedTypingFiles)); for (const newMissingTypingName of newMissingTypingNames) { notFoundTypingNames.push(newMissingTypingName); } From a7a13296e6b1ecfb170f84d34ebd3fa456cdf87e Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 8 Mar 2016 17:40:03 -0800 Subject: [PATCH 19/34] CR feedback --- src/compiler/commandLineParser.ts | 10 +++++----- src/services/jsTyping.ts | 12 +++--------- 2 files changed, 8 insertions(+), 14 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 11b9576c87a..264b2339013 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -590,10 +590,10 @@ namespace ts { } } else if (id === "include") { - options.include = ConvertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); } else if (id === "exclude") { - options.exclude = ConvertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + options.exclude = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); } else { errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id)); @@ -636,9 +636,9 @@ namespace ts { } } if (opt.isFilePath) { - value = normalizePath(combinePaths(basePath, value)); + value = normalizePath(combinePaths(basePath, value)); if (value === "") { - value = "."; + value = "."; } } options[opt.name] = value; @@ -655,7 +655,7 @@ namespace ts { return { options, errors }; } - function ConvertJsonOptionToStringArray(optionName: string, optionJson: any, errors: Diagnostic[], func?: (element: string) => string): string[] { + function convertJsonOptionToStringArray(optionName: string, optionJson: any, errors: Diagnostic[], func?: (element: string) => string): string[] { const items: string[] = []; let invalidOptionType = false; if (!isArray(optionJson)) { diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index b3955e89148..59918ab2445 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -193,8 +193,8 @@ namespace ts.JsTyping { mergeTypings(filter(cleanedTypingNames, f => hasProperty(safeList, f))); } - const jsxFileNames = filter(fileNames, f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX)); - if (jsxFileNames.length > 0) { + const hasJsxFile = forEach(fileNames, f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JSX)); + if (hasJsxFile) { mergeTypings(["react"]); } } @@ -214,6 +214,7 @@ namespace ts.JsTyping { filter( host.readDirectory(nodeModulesPath, /*extension*/ undefined, /*exclude*/ undefined, /*depth*/ 2), f => ts.getBaseFileName(f) === "package.json"); + for (const packageJsonFile of packageJsonFiles) { const packageJsonDict = tryParseJson(packageJsonFile, host); if (!packageJsonDict) { continue; } @@ -247,13 +248,6 @@ namespace ts.JsTyping { if (!options) { return; } - - if (options.jsx === JsxEmit.React) { - typingNames.push("react"); - } - if (options.moduleResolution === ModuleResolutionKind.NodeJs) { - typingNames.push("node"); - } mergeTypings(typingNames); } } From ec7302e6d889d2d87e7966270fdb58750d852254 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Fri, 26 Feb 2016 15:33:34 -0800 Subject: [PATCH 20/34] Adding optionalDependencies and peerDependencies to the list typings to merge in if present. --- src/services/jsTyping.ts | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 59918ab2445..a3268880905 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -173,6 +173,12 @@ namespace ts.JsTyping { if (hasProperty(jsonDict, "devDependencies")) { mergeTypings(getKeys(jsonDict.devDependencies)); } + if (hasProperty(jsonDict, "optionalDependencies")) { + mergeTypings(getKeys(jsonDict.optionalDependencies)); + } + if (hasProperty(jsonDict, "peerDependencies")) { + mergeTypings(getKeys(jsonDict.peerDependencies)); + } } } From 1dc57f23c7cf6999767346f6bf11f7a4a54f37d1 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Mon, 29 Feb 2016 08:14:00 -0800 Subject: [PATCH 21/34] - Removing ts. from jsTyping.js - Adding ".json" file extension filter when retrieving json files from host and removoing filter - simplify isTypingEnabled check --- src/services/jsTyping.ts | 54 +++++++++++++++------------------------- 1 file changed, 20 insertions(+), 34 deletions(-) diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index a3268880905..352b6be77e2 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -29,17 +29,6 @@ namespace ts.JsTyping { return undefined; } - function isTypingEnabled(options: TypingOptions): boolean { - if (options) { - if (options.enableAutoDiscovery || - (options.include && options.include.length > 0) || - (options.exclude && options.exclude.length > 0)) { - return true; - } - } - return false; - } - /** * @param host is the object providing I/O related operations. * @param fileNames are the file names that belong to the same project. @@ -60,15 +49,15 @@ namespace ts.JsTyping { // A typing name to typing file path mapping const inferredTypings: Map = {}; - if (!isTypingEnabled(typingOptions)) { + if (!typingOptions || !typingOptions.enableAutoDiscovery) { return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; } const cachePath = projectRootPath || globalCachePath; // Only infer typings for .js and .jsx files - fileNames = filter(map(fileNames, ts.normalizePath), f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX)); + fileNames = filter(map(fileNames, normalizePath), f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX)); - const safeListFilePath = ts.combinePaths(globalCachePath, "safeList.json"); + const safeListFilePath = combinePaths(globalCachePath, "safeList.json"); if (!safeList && host.fileExists(safeListFilePath)) { safeList = tryParseJson(safeListFilePath, host); } @@ -82,19 +71,19 @@ namespace ts.JsTyping { exclude = typingOptions.exclude || []; if (typingOptions.enableAutoDiscovery) { - const possibleSearchDirs = map(fileNames, ts.getDirectoryPath); + const possibleSearchDirs = map(fileNames, getDirectoryPath); if (projectRootPath !== undefined) { possibleSearchDirs.push(projectRootPath); } - searchDirs = ts.deduplicate(possibleSearchDirs); + searchDirs = deduplicate(possibleSearchDirs); for (const searchDir of searchDirs) { - const packageJsonPath = ts.combinePaths(searchDir, "package.json"); + const packageJsonPath = combinePaths(searchDir, "package.json"); getTypingNamesFromJson(packageJsonPath, filesToWatch); - const bowerJsonPath = ts.combinePaths(searchDir, "bower.json"); + const bowerJsonPath = combinePaths(searchDir, "bower.json"); getTypingNamesFromJson(bowerJsonPath, filesToWatch); - const nodeModulesPath = ts.combinePaths(searchDir, "node_modules"); + const nodeModulesPath = combinePaths(searchDir, "node_modules"); getTypingNamesFromNodeModuleFolder(nodeModulesPath, filesToWatch); } @@ -102,8 +91,8 @@ namespace ts.JsTyping { getTypingNamesFromCompilerOptions(compilerOptions); } - const typingsPath = ts.combinePaths(cachePath, "typings"); - const tsdJsonPath = ts.combinePaths(cachePath, "tsd.json"); + const typingsPath = combinePaths(cachePath, "typings"); + const tsdJsonPath = combinePaths(cachePath, "tsd.json"); const tsdJsonDict = tryParseJson(tsdJsonPath, host); if (tsdJsonDict) { for (const notFoundTypingName of notFoundTypingNames) { @@ -122,7 +111,7 @@ namespace ts.JsTyping { // If the inferred[cachedTypingName] is already not null, which means we found a corresponding // d.ts file that coming with the package. That one should take higher priority. if (hasProperty(inferredTypings, cachedTypingName) && !inferredTypings[cachedTypingName]) { - inferredTypings[cachedTypingName] = ts.combinePaths(typingsPath, cachedTypingPath); + inferredTypings[cachedTypingName] = combinePaths(typingsPath, cachedTypingPath); } } } @@ -190,7 +179,7 @@ namespace ts.JsTyping { */ function getTypingNamesFromSourceFileNames(fileNames: string[]) { const jsFileNames = filter(fileNames, hasJavaScriptFileExtension); - const inferredTypingNames = map(jsFileNames, f => ts.removeFileExtension(ts.getBaseFileName(f.toLowerCase()))); + const inferredTypingNames = map(jsFileNames, f => removeFileExtension(getBaseFileName(f.toLowerCase()))); const cleanedTypingNames = map(inferredTypingNames, f => f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, "")); if (safeList === undefined) { mergeTypings(cleanedTypingNames); @@ -216,16 +205,13 @@ namespace ts.JsTyping { } const typingNames: string[] = []; - const packageJsonFiles = - filter( - host.readDirectory(nodeModulesPath, /*extension*/ undefined, /*exclude*/ undefined, /*depth*/ 2), - f => ts.getBaseFileName(f) === "package.json"); - - for (const packageJsonFile of packageJsonFiles) { - const packageJsonDict = tryParseJson(packageJsonFile, host); + const jsonFiles = host.readDirectory(nodeModulesPath, "*.json", /*exclude*/ undefined, /*depth*/ 2); + for (const jsonFile of jsonFiles) { + if (getBaseFileName(jsonFile) !== "package.json") { continue; } + const packageJsonDict = tryParseJson(jsonFile, host); if (!packageJsonDict) { continue; } - filesToWatch.push(packageJsonFile); + filesToWatch.push(jsonFile); // npm 3 has the package.json contains a "_requiredBy" field // we should include all the top level module names for npm 2, and only module names whose @@ -239,8 +225,8 @@ namespace ts.JsTyping { // to download d.ts files from DefinitelyTyped const packageName = packageJsonDict["name"]; if (hasProperty(packageJsonDict, "typings")) { - const absPath = ts.getNormalizedAbsolutePath(packageJsonDict.typings, ts.getDirectoryPath(packageJsonFile)); - inferredTypings[packageName] = absPath; + const absolutePath = getNormalizedAbsolutePath(packageJsonDict.typings, getDirectoryPath(jsonFile)); + inferredTypings[packageName] = absolutePath; } else { typingNames.push(packageName); @@ -267,7 +253,7 @@ namespace ts.JsTyping { * @param host The object providing I/O related operations. */ export function updateNotFoundTypingNames(newTypingNames: string[], cachePath: string, host: TypingResolutionHost): void { - const tsdJsonPath = ts.combinePaths(cachePath, "tsd.json"); + const tsdJsonPath = combinePaths(cachePath, "tsd.json"); const cacheTsdJsonDict = tryParseJson(tsdJsonPath, host); if (cacheTsdJsonDict) { const installedTypingFiles = hasProperty(cacheTsdJsonDict, "installed") From 085002b0604b7795a5d2871d3ddcf76e2f89cbc6 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 1 Mar 2016 11:50:27 -0800 Subject: [PATCH 22/34] - replacing TryParseJson with existing readConfig - push error for invalid enableAutoDiscovery option - adding interfaces for jsons - removing updateNotFoundTypings - node_modules normalize file names before using - adding safeListPath to discoverTypings --- src/compiler/commandLineParser.ts | 5 +- src/services/jsTyping.ts | 157 ++++++++++++------------------ src/services/shims.ts | 18 +--- 3 files changed, 70 insertions(+), 110 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index 264b2339013..ec86d4f4288 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -477,7 +477,7 @@ namespace ts { * * This method replace comment content by whitespace rather than completely remove them to keep positions in json parsing error reporting accurate. */ - export function removeComments(jsonText: string): string { + function removeComments(jsonText: string): string { let output = ""; const scanner = createScanner(ScriptTarget.ES5, /* skipTrivia */ false, LanguageVariant.Standard, jsonText); let token: SyntaxKind; @@ -588,6 +588,9 @@ namespace ts { if (typeof jsonTypingOptions[id] === "boolean") { options.enableAutoDiscovery = jsonTypingOptions[id]; } + else { + errors.push(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id)); + } } else if (id === "include") { options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 352b6be77e2..786a199eb43 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -13,35 +13,47 @@ namespace ts.JsTyping { readDirectory: (path: string, extension?: string, exclude?: string[], depth?: number) => string[]; }; + interface TsdJson { + version: string; + repo: string; + ref: string; + path: string; + installed?: Map; + }; + + interface TsdInstalledItem { + commit: string; + }; + + interface PackageJson { + _requiredBy?: string[]; + dependencies?: Map; + devDependencies?: Map; + name: string; + optionalDependencies?: Map; + peerDependencies?: Map; + typings?: string; + }; + // A map of loose file names to library names // that we are confident require typings let safeList: Map; - const notFoundTypingNames: string[] = []; - - function tryParseJson(jsonPath: string, host: TypingResolutionHost): any { - if (host.fileExists(jsonPath)) { - try { - const contents = removeComments(host.readFile(jsonPath)); - return JSON.parse(contents); - } - catch (e) { } - } - return undefined; - } /** * @param host is the object providing I/O related operations. * @param fileNames are the file names that belong to the same project. - * @param globalCachePath is used to get the safe list file path and as cache path if the project root path isn't specified. - * @param projectRootPath is the path to the project root directory. This is used for the local typings cache. + * @param cachePath is the path to the typings cache + * @param projectRootPath is the path to the project root directory + * @param safeListPath is the path used to retrieve the safe list * @param typingOptions are used for customizing the typing inference process. * @param compilerOptions are used as a source of typing inference. */ export function discoverTypings( host: TypingResolutionHost, fileNames: string[], - globalCachePath: Path, + cachePath: Path, projectRootPath: Path, + safeListPath: Path, typingOptions: TypingOptions, compilerOptions: CompilerOptions): { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { @@ -53,13 +65,12 @@ namespace ts.JsTyping { return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; } - const cachePath = projectRootPath || globalCachePath; // Only infer typings for .js and .jsx files fileNames = filter(map(fileNames, normalizePath), f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX)); - const safeListFilePath = combinePaths(globalCachePath, "safeList.json"); - if (!safeList && host.fileExists(safeListFilePath)) { - safeList = tryParseJson(safeListFilePath, host); + if (!safeList) { + const result = readConfigFile(safeListPath, host.readFile); + if (result.config) { safeList = result.config; } } const filesToWatch: string[] = []; @@ -86,26 +97,20 @@ namespace ts.JsTyping { const nodeModulesPath = combinePaths(searchDir, "node_modules"); getTypingNamesFromNodeModuleFolder(nodeModulesPath, filesToWatch); } - getTypingNamesFromSourceFileNames(fileNames); - getTypingNamesFromCompilerOptions(compilerOptions); } const typingsPath = combinePaths(cachePath, "typings"); const tsdJsonPath = combinePaths(cachePath, "tsd.json"); - const tsdJsonDict = tryParseJson(tsdJsonPath, host); - if (tsdJsonDict) { - for (const notFoundTypingName of notFoundTypingNames) { - if (hasProperty(inferredTypings, notFoundTypingName) && !inferredTypings[notFoundTypingName]) { - delete inferredTypings[notFoundTypingName]; - } - } + const result = readConfigFile(tsdJsonPath, host.readFile); + if (result.config) { + const tsdJson: TsdJson = result.config; // The "installed" property in the tsd.json serves as a registry of installed typings. Each item // of this object has a key of the relative file path, and a value that contains the corresponding // commit hash. - if (hasProperty(tsdJsonDict, "installed")) { - for (const cachedTypingPath in tsdJsonDict.installed) { + if (tsdJson.installed) { + for (const cachedTypingPath in tsdJson.installed) { // Assuming the cachedTypingPath has the format of "[package name]/[file name]" const cachedTypingName = cachedTypingPath.substr(0, cachedTypingPath.indexOf("/")); // If the inferred[cachedTypingName] is already not null, which means we found a corresponding @@ -153,20 +158,21 @@ namespace ts.JsTyping { * Get the typing info from common package manager json files like package.json or bower.json */ function getTypingNamesFromJson(jsonPath: string, filesToWatch: string[]) { - const jsonDict = tryParseJson(jsonPath, host); - if (jsonDict) { + const result = readConfigFile(jsonPath, host.readFile); + if (result.config) { + const jsonConfig: PackageJson = result.config; filesToWatch.push(jsonPath); - if (hasProperty(jsonDict, "dependencies")) { - mergeTypings(getKeys(jsonDict.dependencies)); + if (jsonConfig.dependencies) { + mergeTypings(getKeys(jsonConfig.dependencies)); } - if (hasProperty(jsonDict, "devDependencies")) { - mergeTypings(getKeys(jsonDict.devDependencies)); + if (jsonConfig.devDependencies) { + mergeTypings(getKeys(jsonConfig.devDependencies)); } - if (hasProperty(jsonDict, "optionalDependencies")) { - mergeTypings(getKeys(jsonDict.optionalDependencies)); + if (jsonConfig.optionalDependencies) { + mergeTypings(getKeys(jsonConfig.optionalDependencies)); } - if (hasProperty(jsonDict, "peerDependencies")) { - mergeTypings(getKeys(jsonDict.peerDependencies)); + if (jsonConfig.peerDependencies) { + mergeTypings(getKeys(jsonConfig.peerDependencies)); } } } @@ -205,75 +211,36 @@ namespace ts.JsTyping { } const typingNames: string[] = []; - const jsonFiles = host.readDirectory(nodeModulesPath, "*.json", /*exclude*/ undefined, /*depth*/ 2); - for (const jsonFile of jsonFiles) { - if (getBaseFileName(jsonFile) !== "package.json") { continue; } - const packageJsonDict = tryParseJson(jsonFile, host); - if (!packageJsonDict) { continue; } + const fileNames = host.readDirectory(nodeModulesPath, "*.json", /*exclude*/ undefined, /*depth*/ 2); + for (const fileName of fileNames) { + const normalizedFileName = normalizePath(fileName); + if (getBaseFileName(normalizedFileName) !== "package.json") { continue; } + const result = readConfigFile(normalizedFileName, host.readFile); + if (!result.config) { continue; } + const packageJson: PackageJson = result.config; + filesToWatch.push(normalizedFileName); - filesToWatch.push(jsonFile); - - // npm 3 has the package.json contains a "_requiredBy" field + // npm 3's package.json contains a "_requiredBy" field // we should include all the top level module names for npm 2, and only module names whose // "_requiredBy" field starts with "#" or equals "/" for npm 3. - if (packageJsonDict._requiredBy && - filter(packageJsonDict._requiredBy, (r: string) => r[0] === "#" || r === "/").length === 0) { + if (packageJson._requiredBy && + filter(packageJson._requiredBy, (r: string) => r[0] === "#" || r === "/").length === 0) { continue; } // If the package has its own d.ts typings, those will take precedence. Otherwise the package name will be used // to download d.ts files from DefinitelyTyped - const packageName = packageJsonDict["name"]; - if (hasProperty(packageJsonDict, "typings")) { - const absolutePath = getNormalizedAbsolutePath(packageJsonDict.typings, getDirectoryPath(jsonFile)); - inferredTypings[packageName] = absolutePath; + if (!packageJson.name) { continue; } + if (packageJson.typings) { + const absolutePath = getNormalizedAbsolutePath(packageJson.typings, getDirectoryPath(normalizedFileName)); + inferredTypings[packageJson.name] = absolutePath; } else { - typingNames.push(packageName); + typingNames.push(packageJson.name); } } mergeTypings(typingNames); } - function getTypingNamesFromCompilerOptions(options: CompilerOptions) { - const typingNames: string[] = []; - if (!options) { - return; - } - mergeTypings(typingNames); - } - } - - /** - * Keep a list of typings names that we know cannot be obtained at the moment (could be because - * of network issues or because the package doesn't hava a d.ts file in DefinitelyTyped), so - * that we won't try again next time within this session. - * @param newTypingNames The list of new typings that the host attempted to acquire - * @param cachePath The path to the tsd.json cache - * @param host The object providing I/O related operations. - */ - export function updateNotFoundTypingNames(newTypingNames: string[], cachePath: string, host: TypingResolutionHost): void { - const tsdJsonPath = combinePaths(cachePath, "tsd.json"); - const cacheTsdJsonDict = tryParseJson(tsdJsonPath, host); - if (cacheTsdJsonDict) { - const installedTypingFiles = hasProperty(cacheTsdJsonDict, "installed") - ? getKeys(cacheTsdJsonDict.installed) - : []; - const newMissingTypingNames = - filter(newTypingNames, name => notFoundTypingNames.indexOf(name) < 0 && !isInstalled(name, installedTypingFiles)); - for (const newMissingTypingName of newMissingTypingNames) { - notFoundTypingNames.push(newMissingTypingName); - } - } - } - - function isInstalled(typing: string, installedKeys: string[]) { - const typingPrefix = typing + "/"; - for (const key of installedKeys) { - if (key.indexOf(typingPrefix) === 0) { - return true; - } - } - return false; } } diff --git a/src/services/shims.ts b/src/services/shims.ts index 19512a49b8e..dea8d6494ef 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -230,8 +230,7 @@ namespace ts { getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getDefaultCompilationSettings(): string; - discoverTypings(fileNamesJson: string, globalCachePath: string, projectRootPath: string, typingOptionsJson: string, compilerOptionsJson: string): string; - updateNotFoundTypingNames(newTypingsJson: string, globalCachePath: string, projectRootPath: string): string; + discoverTypings(fileNamesJson: string, cachePath: string, projectRootPath: string, safeListPath: string, typingOptionsJson: string, compilerOptionsJson: string): string; } function logInternalError(logger: Logger, err: Error) { @@ -986,31 +985,22 @@ namespace ts { ); } - public discoverTypings(fileNamesJson: string, globalCachePath: string, projectRootPath: string, typingOptionsJson: string, compilerOptionsJson: string): string { + public discoverTypings(fileNamesJson: string, cachePath: string, projectRootPath: string, safeListPath: string, typingOptionsJson: string, compilerOptionsJson: string): string { const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false); return this.forwardJSONCall("discoverTypings()", () => { - const cachePath = projectRootPath ? projectRootPath : globalCachePath; const typingOptions = JSON.parse(typingOptionsJson); - const compilerOptions = JSON.parse(compilerOptionsJson); const fileNames: string[] = JSON.parse(fileNamesJson); return ts.JsTyping.discoverTypings( this.host, fileNames, - toPath(globalCachePath, globalCachePath, getCanonicalFileName), toPath(cachePath, cachePath, getCanonicalFileName), + toPath(projectRootPath, projectRootPath, getCanonicalFileName), + toPath(safeListPath, safeListPath, getCanonicalFileName), typingOptions, compilerOptions); }); } - - public updateNotFoundTypingNames(newTypingsJson: string, globalCachePath: string, projectRootPath: string): string { - return this.forwardJSONCall("updateNotFoundTypingNames()", () => { - const newTypingNames: string[] = JSON.parse(newTypingsJson); - const cachePath = projectRootPath ? projectRootPath : globalCachePath; - ts.JsTyping.updateNotFoundTypingNames(newTypingNames, cachePath, this.host); - }); - } } export class TypeScriptServicesFactory implements ShimFactory { From 1fde40957275294bbceacf33176d81d03473a9f0 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 1 Mar 2016 18:52:11 -0800 Subject: [PATCH 23/34] - Adding DiscoverTypingsSettings - Remove all references to Tsd. Instead pass a map of package names to cached typing locations --- src/compiler/types.ts | 12 +++++- src/services/jsTyping.ts | 82 ++++++++++++++-------------------------- src/services/shims.ts | 21 +++++----- 3 files changed, 49 insertions(+), 66 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 946a68ca655..563f2711050 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2453,7 +2453,17 @@ namespace ts { enableAutoDiscovery?: boolean; include?: string[]; exclude?: string[]; - [option: string]: any; + [option: string]: string[] | boolean; + } + + export interface DiscoverTypingsSettings { + fileNames: string[]; // The file names that belong to the same project. + cachePath: string; // The path to the typings cache + projectRootPath: string; // The path to the project root directory + safeListPath: string; // The path used to retrieve the safe list + packageNameToTypingLocation: Map; // The map of package names to their cached typing locations + typingOptions: TypingOptions; // Used to customize the typing inference process + compilerOptions: CompilerOptions; // Used as a source for typing inference } export const enum ModuleKind { diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 786a199eb43..3866296e2f8 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -13,23 +13,11 @@ namespace ts.JsTyping { readDirectory: (path: string, extension?: string, exclude?: string[], depth?: number) => string[]; }; - interface TsdJson { - version: string; - repo: string; - ref: string; - path: string; - installed?: Map; - }; - - interface TsdInstalledItem { - commit: string; - }; - interface PackageJson { _requiredBy?: string[]; dependencies?: Map; devDependencies?: Map; - name: string; + name?: string; optionalDependencies?: Map; peerDependencies?: Map; typings?: string; @@ -41,12 +29,13 @@ namespace ts.JsTyping { /** * @param host is the object providing I/O related operations. - * @param fileNames are the file names that belong to the same project. + * @param fileNames are the file names that belong to the same project * @param cachePath is the path to the typings cache * @param projectRootPath is the path to the project root directory * @param safeListPath is the path used to retrieve the safe list - * @param typingOptions are used for customizing the typing inference process. - * @param compilerOptions are used as a source of typing inference. + * @param packageNameToTypingLocation is the map of package names to their cached typing locations + * @param typingOptions are used to customize the typing inference process + * @param compilerOptions are used as a source for typing inference */ export function discoverTypings( host: TypingResolutionHost, @@ -54,6 +43,7 @@ namespace ts.JsTyping { cachePath: Path, projectRootPath: Path, safeListPath: Path, + packageNameToTypingLocation: Map, typingOptions: TypingOptions, compilerOptions: CompilerOptions): { cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } { @@ -69,8 +59,9 @@ namespace ts.JsTyping { fileNames = filter(map(fileNames, normalizePath), f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX)); if (!safeList) { - const result = readConfigFile(safeListPath, host.readFile); + const result = readConfigFile(safeListPath, (path: string) => host.readFile(path)); if (result.config) { safeList = result.config; } + else { safeList = {}; }; } const filesToWatch: string[] = []; @@ -81,44 +72,27 @@ namespace ts.JsTyping { mergeTypings(typingOptions.include); exclude = typingOptions.exclude || []; - if (typingOptions.enableAutoDiscovery) { - const possibleSearchDirs = map(fileNames, getDirectoryPath); - if (projectRootPath !== undefined) { - possibleSearchDirs.push(projectRootPath); - } - searchDirs = deduplicate(possibleSearchDirs); - for (const searchDir of searchDirs) { - const packageJsonPath = combinePaths(searchDir, "package.json"); - getTypingNamesFromJson(packageJsonPath, filesToWatch); - - const bowerJsonPath = combinePaths(searchDir, "bower.json"); - getTypingNamesFromJson(bowerJsonPath, filesToWatch); - - const nodeModulesPath = combinePaths(searchDir, "node_modules"); - getTypingNamesFromNodeModuleFolder(nodeModulesPath, filesToWatch); - } - getTypingNamesFromSourceFileNames(fileNames); + const possibleSearchDirs = map(fileNames, getDirectoryPath); + if (projectRootPath !== undefined) { + possibleSearchDirs.push(projectRootPath); } + searchDirs = deduplicate(possibleSearchDirs); + for (const searchDir of searchDirs) { + const packageJsonPath = combinePaths(searchDir, "package.json"); + getTypingNamesFromJson(packageJsonPath, filesToWatch); - const typingsPath = combinePaths(cachePath, "typings"); - const tsdJsonPath = combinePaths(cachePath, "tsd.json"); - const result = readConfigFile(tsdJsonPath, host.readFile); - if (result.config) { - const tsdJson: TsdJson = result.config; + const bowerJsonPath = combinePaths(searchDir, "bower.json"); + getTypingNamesFromJson(bowerJsonPath, filesToWatch); - // The "installed" property in the tsd.json serves as a registry of installed typings. Each item - // of this object has a key of the relative file path, and a value that contains the corresponding - // commit hash. - if (tsdJson.installed) { - for (const cachedTypingPath in tsdJson.installed) { - // Assuming the cachedTypingPath has the format of "[package name]/[file name]" - const cachedTypingName = cachedTypingPath.substr(0, cachedTypingPath.indexOf("/")); - // If the inferred[cachedTypingName] is already not null, which means we found a corresponding - // d.ts file that coming with the package. That one should take higher priority. - if (hasProperty(inferredTypings, cachedTypingName) && !inferredTypings[cachedTypingName]) { - inferredTypings[cachedTypingName] = combinePaths(typingsPath, cachedTypingPath); - } - } + const nodeModulesPath = combinePaths(searchDir, "node_modules"); + getTypingNamesFromNodeModuleFolder(nodeModulesPath, filesToWatch); + } + getTypingNamesFromSourceFileNames(fileNames); + + // Add the cached typing locations for inferred typings that are already installed + for (const name in packageNameToTypingLocation) { + if (hasProperty(inferredTypings, name) && !inferredTypings[name]) { + inferredTypings[name] = packageNameToTypingLocation[name]; } } @@ -158,7 +132,7 @@ namespace ts.JsTyping { * Get the typing info from common package manager json files like package.json or bower.json */ function getTypingNamesFromJson(jsonPath: string, filesToWatch: string[]) { - const result = readConfigFile(jsonPath, host.readFile); + const result = readConfigFile(jsonPath, (path: string) => host.readFile(path)); if (result.config) { const jsonConfig: PackageJson = result.config; filesToWatch.push(jsonPath); @@ -215,7 +189,7 @@ namespace ts.JsTyping { for (const fileName of fileNames) { const normalizedFileName = normalizePath(fileName); if (getBaseFileName(normalizedFileName) !== "package.json") { continue; } - const result = readConfigFile(normalizedFileName, host.readFile); + const result = readConfigFile(normalizedFileName, (path: string) => host.readFile(path)); if (!result.config) { continue; } const packageJson: PackageJson = result.config; filesToWatch.push(normalizedFileName); diff --git a/src/services/shims.ts b/src/services/shims.ts index dea8d6494ef..e9f519f1803 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -230,7 +230,7 @@ namespace ts { getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string; getDefaultCompilationSettings(): string; - discoverTypings(fileNamesJson: string, cachePath: string, projectRootPath: string, safeListPath: string, typingOptionsJson: string, compilerOptionsJson: string): string; + discoverTypings(discoverTypingsJson: string): string; } function logInternalError(logger: Logger, err: Error) { @@ -985,20 +985,19 @@ namespace ts { ); } - public discoverTypings(fileNamesJson: string, cachePath: string, projectRootPath: string, safeListPath: string, typingOptionsJson: string, compilerOptionsJson: string): string { + public discoverTypings(discoverTypingsJson: string): string { const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false); return this.forwardJSONCall("discoverTypings()", () => { - const typingOptions = JSON.parse(typingOptionsJson); - const compilerOptions = JSON.parse(compilerOptionsJson); - const fileNames: string[] = JSON.parse(fileNamesJson); + const settings = JSON.parse(discoverTypingsJson); return ts.JsTyping.discoverTypings( this.host, - fileNames, - toPath(cachePath, cachePath, getCanonicalFileName), - toPath(projectRootPath, projectRootPath, getCanonicalFileName), - toPath(safeListPath, safeListPath, getCanonicalFileName), - typingOptions, - compilerOptions); + settings.fileNames, + toPath(settings.cachePath, settings.cachePath, getCanonicalFileName), + toPath(settings.projectRootPath, settings.projectRootPath, getCanonicalFileName), + toPath(settings.safeListPath, settings.safeListPath, getCanonicalFileName), + settings.packageNameToTypingLocation, + settings.typingOptions, + settings.compilerOptions); }); } } From 51e86d3762440524c10fbbc0bd53e6e174e7aa42 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 1 Mar 2016 19:06:31 -0800 Subject: [PATCH 24/34] - Removing filesToWatch from getTypingNamesFromNodeModuleFolder. These modules are already installed and are not expected to change --- src/services/jsTyping.ts | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 3866296e2f8..01cc3eed3b1 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -85,7 +85,7 @@ namespace ts.JsTyping { getTypingNamesFromJson(bowerJsonPath, filesToWatch); const nodeModulesPath = combinePaths(searchDir, "node_modules"); - getTypingNamesFromNodeModuleFolder(nodeModulesPath, filesToWatch); + getTypingNamesFromNodeModuleFolder(nodeModulesPath); } getTypingNamesFromSourceFileNames(fileNames); @@ -178,7 +178,7 @@ namespace ts.JsTyping { * Infer typing names from node_module folder * @param nodeModulesPath is the path to the "node_modules" folder */ - function getTypingNamesFromNodeModuleFolder(nodeModulesPath: string, filesToWatch: string[]) { + function getTypingNamesFromNodeModuleFolder(nodeModulesPath: string) { // Todo: add support for ModuleResolutionHost too if (!host.directoryExists(nodeModulesPath)) { return; @@ -192,7 +192,6 @@ namespace ts.JsTyping { const result = readConfigFile(normalizedFileName, (path: string) => host.readFile(path)); if (!result.config) { continue; } const packageJson: PackageJson = result.config; - filesToWatch.push(normalizedFileName); // npm 3's package.json contains a "_requiredBy" field // we should include all the top level module names for npm 2, and only module names whose From 900b0a7b1840ba82b46ccbdd5a656907b81d6374 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Wed, 2 Mar 2016 10:11:13 -0800 Subject: [PATCH 25/34] - Adding new lines after { for single-line if statements - Renaming DiscoverTypingsSettings to DiscoverTypingsInfo to match host --- src/compiler/types.ts | 2 +- src/services/jsTyping.ts | 20 +++++++++++++++----- src/services/shims.ts | 16 ++++++++-------- 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 563f2711050..5f5cc461dbb 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2456,7 +2456,7 @@ namespace ts { [option: string]: string[] | boolean; } - export interface DiscoverTypingsSettings { + export interface DiscoverTypingsInfo { fileNames: string[]; // The file names that belong to the same project. cachePath: string; // The path to the typings cache projectRootPath: string; // The path to the project root directory diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 01cc3eed3b1..22265cc1016 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -60,8 +60,12 @@ namespace ts.JsTyping { if (!safeList) { const result = readConfigFile(safeListPath, (path: string) => host.readFile(path)); - if (result.config) { safeList = result.config; } - else { safeList = {}; }; + if (result.config) { + safeList = result.config; + } + else { + safeList = {}; + }; } const filesToWatch: string[] = []; @@ -188,9 +192,13 @@ namespace ts.JsTyping { const fileNames = host.readDirectory(nodeModulesPath, "*.json", /*exclude*/ undefined, /*depth*/ 2); for (const fileName of fileNames) { const normalizedFileName = normalizePath(fileName); - if (getBaseFileName(normalizedFileName) !== "package.json") { continue; } + if (getBaseFileName(normalizedFileName) !== "package.json") { + continue; + } const result = readConfigFile(normalizedFileName, (path: string) => host.readFile(path)); - if (!result.config) { continue; } + if (!result.config) { + continue; + } const packageJson: PackageJson = result.config; // npm 3's package.json contains a "_requiredBy" field @@ -203,7 +211,9 @@ namespace ts.JsTyping { // If the package has its own d.ts typings, those will take precedence. Otherwise the package name will be used // to download d.ts files from DefinitelyTyped - if (!packageJson.name) { continue; } + if (!packageJson.name) { + continue; + } if (packageJson.typings) { const absolutePath = getNormalizedAbsolutePath(packageJson.typings, getDirectoryPath(normalizedFileName)); inferredTypings[packageJson.name] = absolutePath; diff --git a/src/services/shims.ts b/src/services/shims.ts index e9f519f1803..bf11b18b279 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -988,16 +988,16 @@ namespace ts { public discoverTypings(discoverTypingsJson: string): string { const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false); return this.forwardJSONCall("discoverTypings()", () => { - const settings = JSON.parse(discoverTypingsJson); + const info = JSON.parse(discoverTypingsJson); return ts.JsTyping.discoverTypings( this.host, - settings.fileNames, - toPath(settings.cachePath, settings.cachePath, getCanonicalFileName), - toPath(settings.projectRootPath, settings.projectRootPath, getCanonicalFileName), - toPath(settings.safeListPath, settings.safeListPath, getCanonicalFileName), - settings.packageNameToTypingLocation, - settings.typingOptions, - settings.compilerOptions); + info.fileNames, + toPath(info.cachePath, info.cachePath, getCanonicalFileName), + toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName), + toPath(info.safeListPath, info.safeListPath, getCanonicalFileName), + info.packageNameToTypingLocation, + info.typingOptions, + info.compilerOptions); }); } } From e5dee7eb4bcd00692a338c079ff9d38a4ef054ee Mon Sep 17 00:00:00 2001 From: Basarat Ali Syed Date: Thu, 3 Mar 2016 10:32:07 +1100 Subject: [PATCH 26/34] fix(build) `TypingResolutionHost` interface is used by exported function `discoverTypings` --- src/services/jsTyping.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 22265cc1016..b78434ddaa0 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -6,7 +6,7 @@ /* @internal */ namespace ts.JsTyping { - interface TypingResolutionHost { + export interface TypingResolutionHost { directoryExists: (path: string) => boolean; fileExists: (fileName: string) => boolean; readFile: (path: string, encoding?: string) => string; From 77f317e27f61d16b3cb5fa21423f306878f34740 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Thu, 3 Mar 2016 15:17:52 -0800 Subject: [PATCH 27/34] Removing cachePath from discoverTypings and DiscoverTypingsInfo. With the move to using the packageNameToLocation map it is no longer required. --- src/compiler/types.ts | 1 - src/services/jsTyping.ts | 2 -- src/services/shims.ts | 1 - 3 files changed, 4 deletions(-) diff --git a/src/compiler/types.ts b/src/compiler/types.ts index 5f5cc461dbb..d0cd0ae6080 100644 --- a/src/compiler/types.ts +++ b/src/compiler/types.ts @@ -2458,7 +2458,6 @@ namespace ts { export interface DiscoverTypingsInfo { fileNames: string[]; // The file names that belong to the same project. - cachePath: string; // The path to the typings cache projectRootPath: string; // The path to the project root directory safeListPath: string; // The path used to retrieve the safe list packageNameToTypingLocation: Map; // The map of package names to their cached typing locations diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index b78434ddaa0..943693bf52e 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -30,7 +30,6 @@ namespace ts.JsTyping { /** * @param host is the object providing I/O related operations. * @param fileNames are the file names that belong to the same project - * @param cachePath is the path to the typings cache * @param projectRootPath is the path to the project root directory * @param safeListPath is the path used to retrieve the safe list * @param packageNameToTypingLocation is the map of package names to their cached typing locations @@ -40,7 +39,6 @@ namespace ts.JsTyping { export function discoverTypings( host: TypingResolutionHost, fileNames: string[], - cachePath: Path, projectRootPath: Path, safeListPath: Path, packageNameToTypingLocation: Map, diff --git a/src/services/shims.ts b/src/services/shims.ts index bf11b18b279..a9ef3054838 100644 --- a/src/services/shims.ts +++ b/src/services/shims.ts @@ -992,7 +992,6 @@ namespace ts { return ts.JsTyping.discoverTypings( this.host, info.fileNames, - toPath(info.cachePath, info.cachePath, getCanonicalFileName), toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName), toPath(info.safeListPath, info.safeListPath, getCanonicalFileName), info.packageNameToTypingLocation, From 11102dc771b6a2262dc6f2fc00008898b1018661 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Tue, 8 Mar 2016 18:38:39 -0800 Subject: [PATCH 28/34] - Adding missing diagnostic message - removing extra semi-colon --- src/compiler/diagnosticMessages.json | 5 ++++- src/services/jsTyping.ts | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/compiler/diagnosticMessages.json b/src/compiler/diagnosticMessages.json index eb6c2bce814..ce9303a85c9 100644 --- a/src/compiler/diagnosticMessages.json +++ b/src/compiler/diagnosticMessages.json @@ -2450,11 +2450,14 @@ "category": "Message", "code": 6084 }, + "Option '{0}' should have array of strings as a value.": { + "category": "Error", + "code": 6103 + }, "Do not emit 'use strict' directives in module output.": { "category": "Message", "code": 6112 }, - "Variable '{0}' implicitly has an '{1}' type.": { "category": "Error", "code": 7005 diff --git a/src/services/jsTyping.ts b/src/services/jsTyping.ts index 943693bf52e..4d4903ab59d 100644 --- a/src/services/jsTyping.ts +++ b/src/services/jsTyping.ts @@ -63,7 +63,7 @@ namespace ts.JsTyping { } else { safeList = {}; - }; + } } const filesToWatch: string[] = []; From fa6db1b8c95e28209a4147838dc7fd2ca433b9df Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Wed, 9 Mar 2016 14:57:55 -0800 Subject: [PATCH 29/34] Addressed feedback --- src/compiler/binder.ts | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/compiler/binder.ts b/src/compiler/binder.ts index e0297145805..dc1c908fbe2 100644 --- a/src/compiler/binder.ts +++ b/src/compiler/binder.ts @@ -278,11 +278,10 @@ namespace ts { function declareSymbol(symbolTable: SymbolTable, parent: Symbol, node: Declaration, includes: SymbolFlags, excludes: SymbolFlags): Symbol { Debug.assert(!hasDynamicName(node)); - const isJsModuleExport = getSpecialPropertyAssignmentKind(node) === SpecialPropertyAssignmentKind.ModuleExports; const isDefaultExport = node.flags & NodeFlags.Default; // The exported symbol for an export default function/class node is always named "default" - const name = isJsModuleExport ? "export=" : isDefaultExport && parent ? "default" : getDeclarationName(node); + const name = isDefaultExport && parent ? "default" : getDeclarationName(node); let symbol: Symbol; if (name !== undefined) { From fe9da694b2c21b41247e0e9b685a190285773729 Mon Sep 17 00:00:00 2001 From: Jason Ramsay Date: Wed, 9 Mar 2016 15:50:24 -0800 Subject: [PATCH 30/34] Fixing a whitespace issue and revving the servicesVerion --- src/compiler/commandLineParser.ts | 2 +- src/services/services.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/compiler/commandLineParser.ts b/src/compiler/commandLineParser.ts index ec86d4f4288..b3a081d6b05 100644 --- a/src/compiler/commandLineParser.ts +++ b/src/compiler/commandLineParser.ts @@ -642,7 +642,7 @@ namespace ts { value = normalizePath(combinePaths(basePath, value)); if (value === "") { value = "."; - } + } } options[opt.name] = value; } diff --git a/src/services/services.ts b/src/services/services.ts index 4f7e2ba8d1e..bdd3db708be 100644 --- a/src/services/services.ts +++ b/src/services/services.ts @@ -13,7 +13,7 @@ namespace ts { /** The version of the language service API */ - export const servicesVersion = "0.4"; + export const servicesVersion = "0.5"; export interface Node { getSourceFile(): SourceFile; From 3eb2bb5f74540e033701d706b3759036c289932a Mon Sep 17 00:00:00 2001 From: zhengbli Date: Thu, 10 Mar 2016 09:22:48 -0800 Subject: [PATCH 31/34] Port #7307 to release-1.8 --- src/services/formatting/formatting.ts | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/services/formatting/formatting.ts b/src/services/formatting/formatting.ts index 53a8c2b4307..e94e6d6fb29 100644 --- a/src/services/formatting/formatting.ts +++ b/src/services/formatting/formatting.ts @@ -72,12 +72,20 @@ namespace ts.formatting { if (line === 0) { return []; } - // get the span for the previous\current line + // After the enter key, the cursor is now at a new line. The new line may or may not contain non-whitespace characters. + // If the new line has only whitespaces, we won't want to format this line, because that would remove the indentation as + // trailing whitespaces. So the end of the formatting span should be the later one between: + // 1. the end of the previous line + // 2. the last non-whitespace character in the current line + let endOfFormatSpan = getEndLinePosition(line, sourceFile); + while (isWhiteSpace(sourceFile.text.charCodeAt(endOfFormatSpan)) && !isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) { + endOfFormatSpan--; + } let span = { // get start position for the previous line pos: getStartPositionOfLine(line - 1, sourceFile), - // get end position for the current line (end value is exclusive so add 1 to the result) - end: getEndLinePosition(line, sourceFile) + 1 + // end value is exclusive so add 1 to the result + end: endOfFormatSpan + 1 } return formatSpan(span, sourceFile, options, rulesProvider, FormattingRequestKind.FormatOnEnter); } From 16d76561603478a3f4648ee483892e7807e88c49 Mon Sep 17 00:00:00 2001 From: Bill Ticehurst Date: Thu, 10 Mar 2016 10:26:53 -0800 Subject: [PATCH 32/34] AllowJS files in tsserver when no project is given --- src/server/editorServices.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/server/editorServices.ts b/src/server/editorServices.ts index 71907735b91..42a2bded6a5 100644 --- a/src/server/editorServices.ts +++ b/src/server/editorServices.ts @@ -1317,6 +1317,7 @@ namespace ts.server { else { const defaultOpts = ts.getDefaultCompilerOptions(); defaultOpts.allowNonTsExtensions = true; + defaultOpts.allowJs = true; this.setCompilerOptions(defaultOpts); } this.languageService = ts.createLanguageService(this.host, this.documentRegistry); From e473d53ecddba3c29c63084646ca121b75d605ff Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Thu, 10 Mar 2016 14:52:10 -0800 Subject: [PATCH 33/34] Update version --- package.json | 2 +- src/compiler/program.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/package.json b/package.json index 3fc0bd67db8..331fcd812fe 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "typescript", "author": "Microsoft Corp.", "homepage": "http://typescriptlang.org/", - "version": "1.8.5", + "version": "1.8.9", "license": "Apache-2.0", "description": "TypeScript is a language for application scale JavaScript development", "keywords": [ diff --git a/src/compiler/program.ts b/src/compiler/program.ts index a76517ecbce..f93801e123a 100644 --- a/src/compiler/program.ts +++ b/src/compiler/program.ts @@ -12,7 +12,7 @@ namespace ts { const emptyArray: any[] = []; - export const version = "1.8.5"; + export const version = "1.8.9"; export function findConfigFile(searchPath: string, fileExists: (fileName: string) => boolean): string { let fileName = "tsconfig.json"; From 4927091060dcd99cd5aba9e67e1162a7fc7a31ac Mon Sep 17 00:00:00 2001 From: Mohamed Hegazy Date: Thu, 10 Mar 2016 14:59:45 -0800 Subject: [PATCH 34/34] Update LKG --- lib/tsc.js | 160 ++++++++++-- lib/tsserver.js | 423 ++++++++++++++++++++++++++----- lib/typescript.d.ts | 31 ++- lib/typescript.js | 493 +++++++++++++++++++++++++++++++----- lib/typescriptServices.d.ts | 31 ++- lib/typescriptServices.js | 493 +++++++++++++++++++++++++++++++----- 6 files changed, 1414 insertions(+), 217 deletions(-) diff --git a/lib/tsc.js b/lib/tsc.js index 8e330ebb650..b007d40e3b7 100644 --- a/lib/tsc.js +++ b/lib/tsc.js @@ -280,6 +280,14 @@ var ts; return hasOwnProperty.call(map, key); } ts.hasProperty = hasProperty; + function getKeys(map) { + var keys = []; + for (var key in map) { + keys.push(key); + } + return keys; + } + ts.getKeys = getKeys; function getProperty(map, key) { return hasOwnProperty.call(map, key) ? map[key] : undefined; } @@ -685,6 +693,26 @@ var ts; return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; } ts.fileExtensionIs = fileExtensionIs; + function ensureScriptKind(fileName, scriptKind) { + return (scriptKind || getScriptKindFromFileName(fileName)) || 3; + } + ts.ensureScriptKind = ensureScriptKind; + function getScriptKindFromFileName(fileName) { + var ext = fileName.substr(fileName.lastIndexOf(".")); + switch (ext.toLowerCase()) { + case ".js": + return 1; + case ".jsx": + return 2; + case ".ts": + return 3; + case ".tsx": + return 4; + default: + return 0; + } + } + ts.getScriptKindFromFileName = getScriptKindFromFileName; ts.supportedTypeScriptExtensions = [".ts", ".tsx", ".d.ts"]; ts.supportedJavascriptExtensions = [".js", ".jsx"]; var allSupportedExtensions = ts.supportedTypeScriptExtensions.concat(ts.supportedJavascriptExtensions); @@ -1844,6 +1872,7 @@ var ts; Only_amd_and_system_modules_are_supported_alongside_0: { code: 6082, category: ts.DiagnosticCategory.Error, key: "Only_amd_and_system_modules_are_supported_alongside_0_6082", message: "Only 'amd' and 'system' modules are supported alongside --{0}." }, Allow_javascript_files_to_be_compiled: { code: 6083, category: ts.DiagnosticCategory.Message, key: "Allow_javascript_files_to_be_compiled_6083", message: "Allow javascript files to be compiled." }, Specifies_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit: { code: 6084, category: ts.DiagnosticCategory.Message, key: "Specifies_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit_6084", message: "Specifies the object invoked for createElement and __spread when targeting 'react' JSX emit" }, + Option_0_should_have_array_of_strings_as_a_value: { code: 6103, category: ts.DiagnosticCategory.Error, key: "Option_0_should_have_array_of_strings_as_a_value_6103", message: "Option '{0}' should have array of strings as a value." }, Do_not_emit_use_strict_directives_in_module_output: { code: 6112, category: ts.DiagnosticCategory.Message, key: "Do_not_emit_use_strict_directives_in_module_output_6112", message: "Do not emit 'use strict' directives in module output." }, Variable_0_implicitly_has_an_1_type: { code: 7005, category: ts.DiagnosticCategory.Error, key: "Variable_0_implicitly_has_an_1_type_7005", message: "Variable '{0}' implicitly has an '{1}' type." }, Parameter_0_implicitly_has_an_1_type: { code: 7006, category: ts.DiagnosticCategory.Error, key: "Parameter_0_implicitly_has_an_1_type_7006", message: "Parameter '{0}' implicitly has an '{1}' type." }, @@ -1894,7 +1923,8 @@ var ts; An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17006, category: ts.DiagnosticCategory.Error, key: "An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_ex_17006", message: "An unary expression with the '{0}' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17007, category: ts.DiagnosticCategory.Error, key: "A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Con_17007", message: "A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, JSX_element_0_has_no_corresponding_closing_tag: { code: 17008, category: ts.DiagnosticCategory.Error, key: "JSX_element_0_has_no_corresponding_closing_tag_17008", message: "JSX element '{0}' has no corresponding closing tag." }, - super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class: { code: 17009, category: ts.DiagnosticCategory.Error, key: "super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class_17009", message: "'super' must be called before accessing 'this' in the constructor of a derived class." } + super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class: { code: 17009, category: ts.DiagnosticCategory.Error, key: "super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class_17009", message: "'super' must be called before accessing 'this' in the constructor of a derived class." }, + Unknown_typing_option_0: { code: 17010, category: ts.DiagnosticCategory.Error, key: "Unknown_typing_option_0_17010", message: "Unknown typing option '{0}'." } }; })(ts || (ts = {})); var ts; @@ -4288,6 +4318,9 @@ var ts; } ts.isRequireCall = isRequireCall; function getSpecialPropertyAssignmentKind(expression) { + if (!isInJavaScriptFile(expression)) { + return 0; + } if (expression.kind !== 184) { return 0; } @@ -6052,10 +6085,10 @@ var ts; } } ts.forEachChild = forEachChild; - function createSourceFile(fileName, sourceText, languageVersion, setParentNodes) { + function createSourceFile(fileName, sourceText, languageVersion, setParentNodes, scriptKind) { if (setParentNodes === void 0) { setParentNodes = false; } var start = new Date().getTime(); - var result = Parser.parseSourceFile(fileName, sourceText, languageVersion, undefined, setParentNodes); + var result = Parser.parseSourceFile(fileName, sourceText, languageVersion, undefined, setParentNodes, scriptKind); ts.parseTime += new Date().getTime() - start; return result; } @@ -6089,18 +6122,18 @@ var ts; var parsingContext; var contextFlags; var parseErrorBeforeNextFinishedNode = false; - function parseSourceFile(fileName, _sourceText, languageVersion, _syntaxCursor, setParentNodes) { - var isJavaScriptFile = ts.hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0; - initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); - var result = parseSourceFileWorker(fileName, languageVersion, setParentNodes); + function parseSourceFile(fileName, _sourceText, languageVersion, _syntaxCursor, setParentNodes, scriptKind) { + scriptKind = ts.ensureScriptKind(fileName, scriptKind); + initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind); + var result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind); clearState(); return result; } Parser.parseSourceFile = parseSourceFile; - function getLanguageVariant(fileName) { - return ts.fileExtensionIs(fileName, ".tsx") || ts.fileExtensionIs(fileName, ".jsx") || ts.fileExtensionIs(fileName, ".js") ? 1 : 0; + function getLanguageVariant(scriptKind) { + return scriptKind === 4 || scriptKind === 2 || scriptKind === 1 ? 1 : 0; } - function initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor) { + function initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind) { NodeConstructor = ts.objectAllocator.getNodeConstructor(); SourceFileConstructor = ts.objectAllocator.getSourceFileConstructor(); sourceText = _sourceText; @@ -6110,12 +6143,12 @@ var ts; identifiers = {}; identifierCount = 0; nodeCount = 0; - contextFlags = isJavaScriptFile ? 32 : 0; + contextFlags = scriptKind === 1 || scriptKind === 2 ? 32 : 0; parseErrorBeforeNextFinishedNode = false; scanner.setText(sourceText); scanner.setOnError(scanError); scanner.setScriptTarget(languageVersion); - scanner.setLanguageVariant(getLanguageVariant(fileName)); + scanner.setLanguageVariant(getLanguageVariant(scriptKind)); } function clearState() { scanner.setText(""); @@ -6126,8 +6159,8 @@ var ts; syntaxCursor = undefined; sourceText = undefined; } - function parseSourceFileWorker(fileName, languageVersion, setParentNodes) { - sourceFile = createSourceFile(fileName, languageVersion); + function parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind) { + sourceFile = createSourceFile(fileName, languageVersion, scriptKind); if (contextFlags & 32) { sourceFile.parserContextFlags = 32; } @@ -6176,7 +6209,7 @@ var ts; } } Parser.fixupParentReferences = fixupParentReferences; - function createSourceFile(fileName, languageVersion) { + function createSourceFile(fileName, languageVersion, scriptKind) { var sourceFile = new SourceFileConstructor(251, 0, sourceText.length); nodeCount++; sourceFile.text = sourceText; @@ -6184,7 +6217,8 @@ var ts; sourceFile.languageVersion = languageVersion; sourceFile.fileName = ts.normalizePath(fileName); sourceFile.flags = ts.fileExtensionIs(sourceFile.fileName, ".d.ts") ? 4096 : 0; - sourceFile.languageVariant = getLanguageVariant(sourceFile.fileName); + sourceFile.languageVariant = getLanguageVariant(scriptKind); + sourceFile.scriptKind = scriptKind; return sourceFile; } function setContextFlag(val, flag) { @@ -6720,7 +6754,7 @@ var ts; if (ts.containsParseError(node)) { return undefined; } - var nodeContextFlags = node.parserContextFlags & 31; + var nodeContextFlags = node.parserContextFlags & 63; if (nodeContextFlags !== contextFlags) { return undefined; } @@ -9597,7 +9631,7 @@ var ts; } JSDocParser.isJSDocType = isJSDocType; function parseJSDocTypeExpressionForTests(content, start, length) { - initializeState("file.js", content, 2, true, undefined); + initializeState("file.js", content, 2, undefined, 1); scanner.setText(content, start, length); token = scanner.scan(); var jsDocTypeExpression = parseJSDocTypeExpression(); @@ -9850,7 +9884,7 @@ var ts; } } function parseIsolatedJSDocComment(content, start, length) { - initializeState("file.js", content, 2, true, undefined); + initializeState("file.js", content, 2, undefined, 1); sourceFile = { languageVariant: 0, text: content }; var jsDocComment = parseJSDocCommentWorker(start, length); var diagnostics = parseDiagnostics; @@ -10103,7 +10137,7 @@ var ts; return sourceFile; } if (sourceFile.statements.length === 0) { - return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, undefined, true); + return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, undefined, true, sourceFile.scriptKind); } var incrementalSourceFile = sourceFile; ts.Debug.assert(!incrementalSourceFile.hasBeenIncrementallyParsed); @@ -10117,7 +10151,7 @@ var ts; ts.Debug.assert(ts.textSpanEnd(ts.textChangeRangeNewSpan(changeRange)) === ts.textSpanEnd(ts.textChangeRangeNewSpan(textChangeRange))); var delta = ts.textChangeRangeNewSpan(changeRange).length - changeRange.span.length; updateTokenPositionsAndMarkElements(incrementalSourceFile, changeRange.span.start, ts.textSpanEnd(changeRange.span), ts.textSpanEnd(ts.textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks); - var result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, true); + var result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, true, sourceFile.scriptKind); return result; } IncrementalParser.updateSourceFile = updateSourceFile; @@ -10872,6 +10906,7 @@ var ts; case 146: case 147: case 153: + case 264: case 154: case 176: case 177: @@ -11362,7 +11397,7 @@ var ts; } function bindModuleExportsAssignment(node) { setCommonJsModuleIndicator(node); - bindExportAssignment(node); + declareSymbol(file.symbol.exports, file.symbol, node, 4 | 7340032 | 512, 0); } function bindThisPropertyAssignment(node) { if (container.kind === 176 || container.kind === 216) { @@ -11384,7 +11419,7 @@ var ts; if (!funcSymbol.members) { funcSymbol.members = {}; } - declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, 4, 107455); + declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, 4, 107455 & ~4); } function bindCallExpression(node) { if (!file.commonJsModuleIndicator && ts.isRequireCall(node, false)) { @@ -12267,7 +12302,9 @@ var ts; function getTargetOfImportClause(node) { var moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier); if (moduleSymbol) { - var exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]); + var exportDefaultSymbol = moduleSymbol.exports["export="] ? + getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : + resolveSymbol(moduleSymbol.exports["default"]); if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, ts.Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); } @@ -12318,8 +12355,14 @@ var ts; if (targetSymbol) { var name_9 = specifier.propertyName || specifier.name; if (name_9.text) { + var symbolFromVariable = void 0; + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { + symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name_9.text); + } + else { + symbolFromVariable = getPropertyOfVariable(targetSymbol, name_9.text); + } var symbolFromModule = getExportOfModule(targetSymbol, name_9.text); - var symbolFromVariable = getPropertyOfVariable(targetSymbol, name_9.text); var symbol = symbolFromModule && symbolFromVariable ? combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; @@ -15780,7 +15823,7 @@ var ts; for (var i = 0; i < checkCount; i++) { var s = i < sourceMax ? getTypeOfSymbol(sourceParams[i]) : getRestTypeOfSignature(source); var t = i < targetMax ? getTypeOfSymbol(targetParams[i]) : getRestTypeOfSignature(target); - var related = compareTypes(t, s, false) || compareTypes(s, t, reportErrors); + var related = compareTypes(s, t, false) || compareTypes(t, s, reportErrors); if (!related) { if (reportErrors) { errorReporter(ts.Diagnostics.Types_of_parameters_0_and_1_are_incompatible, sourceParams[i < sourceMax ? i : sourceMax].name, targetParams[i < targetMax ? i : targetMax].name); @@ -16778,8 +16821,10 @@ var ts; function inferTypes(context, source, target) { var sourceStack; var targetStack; + var maxDepth = 5; var depth = 0; var inferiority = 0; + var visited = {}; inferFromTypes(source, target); function isInProcess(source, target) { for (var i = 0; i < depth; i++) { @@ -16875,9 +16920,17 @@ var ts; if (isInProcess(source, target)) { return; } + if (depth > maxDepth) { + return; + } if (isDeeplyNestedGeneric(source, sourceStack, depth) && isDeeplyNestedGeneric(target, targetStack, depth)) { return; } + var key = source.id + "," + target.id; + if (ts.hasProperty(visited, key)) { + return; + } + visited[key] = true; if (depth === 0) { sourceStack = []; targetStack = []; @@ -32466,7 +32519,7 @@ var ts; ts.ioReadTime = 0; ts.ioWriteTime = 0; var emptyArray = []; - ts.version = "1.8.5"; + ts.version = "1.8.9"; function findConfigFile(searchPath, fileExists) { var fileName = "tsconfig.json"; while (true) { @@ -34025,6 +34078,7 @@ var ts; return { options: options, fileNames: getFileNames(), + typingOptions: getTypingOptions(), errors: errors }; function getFileNames() { @@ -34077,6 +34131,34 @@ var ts; } return fileNames; } + function getTypingOptions() { + var options = ts.getBaseFileName(configFileName) === "jsconfig.json" + ? { enableAutoDiscovery: true, include: [], exclude: [] } + : { enableAutoDiscovery: false, include: [], exclude: [] }; + var jsonTypingOptions = json["typingOptions"]; + if (jsonTypingOptions) { + for (var id in jsonTypingOptions) { + if (id === "enableAutoDiscovery") { + if (typeof jsonTypingOptions[id] === "boolean") { + options.enableAutoDiscovery = jsonTypingOptions[id]; + } + else { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Unknown_typing_option_0, id)); + } + } + else if (id === "include") { + options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + } + else if (id === "exclude") { + options.exclude = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + } + else { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Unknown_typing_option_0, id)); + } + } + } + return options; + } } ts.parseJsonConfigFileContent = parseJsonConfigFileContent; function convertCompilerOptionsFromJson(jsonOptions, basePath, configFileName) { @@ -34125,6 +34207,30 @@ var ts; return { options: options, errors: errors }; } ts.convertCompilerOptionsFromJson = convertCompilerOptionsFromJson; + function convertJsonOptionToStringArray(optionName, optionJson, errors, func) { + var items = []; + var invalidOptionType = false; + if (!ts.isArray(optionJson)) { + invalidOptionType = true; + } + else { + for (var _i = 0, _a = optionJson; _i < _a.length; _i++) { + var element = _a[_i]; + if (typeof element === "string") { + var item = func ? func(element) : element; + items.push(item); + } + else { + invalidOptionType = true; + break; + } + } + } + if (invalidOptionType) { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Option_0_should_have_array_of_strings_as_a_value, optionName)); + } + return items; + } })(ts || (ts = {})); var ts; (function (ts) { diff --git a/lib/tsserver.js b/lib/tsserver.js index 32bf7274dc3..33675d32c9a 100644 --- a/lib/tsserver.js +++ b/lib/tsserver.js @@ -285,6 +285,14 @@ var ts; return hasOwnProperty.call(map, key); } ts.hasProperty = hasProperty; + function getKeys(map) { + var keys = []; + for (var key in map) { + keys.push(key); + } + return keys; + } + ts.getKeys = getKeys; function getProperty(map, key) { return hasOwnProperty.call(map, key) ? map[key] : undefined; } @@ -690,6 +698,26 @@ var ts; return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; } ts.fileExtensionIs = fileExtensionIs; + function ensureScriptKind(fileName, scriptKind) { + return (scriptKind || getScriptKindFromFileName(fileName)) || 3; + } + ts.ensureScriptKind = ensureScriptKind; + function getScriptKindFromFileName(fileName) { + var ext = fileName.substr(fileName.lastIndexOf(".")); + switch (ext.toLowerCase()) { + case ".js": + return 1; + case ".jsx": + return 2; + case ".ts": + return 3; + case ".tsx": + return 4; + default: + return 0; + } + } + ts.getScriptKindFromFileName = getScriptKindFromFileName; ts.supportedTypeScriptExtensions = [".ts", ".tsx", ".d.ts"]; ts.supportedJavascriptExtensions = [".js", ".jsx"]; var allSupportedExtensions = ts.supportedTypeScriptExtensions.concat(ts.supportedJavascriptExtensions); @@ -1849,6 +1877,7 @@ var ts; Only_amd_and_system_modules_are_supported_alongside_0: { code: 6082, category: ts.DiagnosticCategory.Error, key: "Only_amd_and_system_modules_are_supported_alongside_0_6082", message: "Only 'amd' and 'system' modules are supported alongside --{0}." }, Allow_javascript_files_to_be_compiled: { code: 6083, category: ts.DiagnosticCategory.Message, key: "Allow_javascript_files_to_be_compiled_6083", message: "Allow javascript files to be compiled." }, Specifies_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit: { code: 6084, category: ts.DiagnosticCategory.Message, key: "Specifies_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit_6084", message: "Specifies the object invoked for createElement and __spread when targeting 'react' JSX emit" }, + Option_0_should_have_array_of_strings_as_a_value: { code: 6103, category: ts.DiagnosticCategory.Error, key: "Option_0_should_have_array_of_strings_as_a_value_6103", message: "Option '{0}' should have array of strings as a value." }, Do_not_emit_use_strict_directives_in_module_output: { code: 6112, category: ts.DiagnosticCategory.Message, key: "Do_not_emit_use_strict_directives_in_module_output_6112", message: "Do not emit 'use strict' directives in module output." }, Variable_0_implicitly_has_an_1_type: { code: 7005, category: ts.DiagnosticCategory.Error, key: "Variable_0_implicitly_has_an_1_type_7005", message: "Variable '{0}' implicitly has an '{1}' type." }, Parameter_0_implicitly_has_an_1_type: { code: 7006, category: ts.DiagnosticCategory.Error, key: "Parameter_0_implicitly_has_an_1_type_7006", message: "Parameter '{0}' implicitly has an '{1}' type." }, @@ -1899,7 +1928,8 @@ var ts; An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17006, category: ts.DiagnosticCategory.Error, key: "An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_ex_17006", message: "An unary expression with the '{0}' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17007, category: ts.DiagnosticCategory.Error, key: "A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Con_17007", message: "A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, JSX_element_0_has_no_corresponding_closing_tag: { code: 17008, category: ts.DiagnosticCategory.Error, key: "JSX_element_0_has_no_corresponding_closing_tag_17008", message: "JSX element '{0}' has no corresponding closing tag." }, - super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class: { code: 17009, category: ts.DiagnosticCategory.Error, key: "super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class_17009", message: "'super' must be called before accessing 'this' in the constructor of a derived class." } + super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class: { code: 17009, category: ts.DiagnosticCategory.Error, key: "super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class_17009", message: "'super' must be called before accessing 'this' in the constructor of a derived class." }, + Unknown_typing_option_0: { code: 17010, category: ts.DiagnosticCategory.Error, key: "Unknown_typing_option_0_17010", message: "Unknown typing option '{0}'." } }; })(ts || (ts = {})); var ts; @@ -3840,6 +3870,7 @@ var ts; return { options: options, fileNames: getFileNames(), + typingOptions: getTypingOptions(), errors: errors }; function getFileNames() { @@ -3892,6 +3923,34 @@ var ts; } return fileNames; } + function getTypingOptions() { + var options = ts.getBaseFileName(configFileName) === "jsconfig.json" + ? { enableAutoDiscovery: true, include: [], exclude: [] } + : { enableAutoDiscovery: false, include: [], exclude: [] }; + var jsonTypingOptions = json["typingOptions"]; + if (jsonTypingOptions) { + for (var id in jsonTypingOptions) { + if (id === "enableAutoDiscovery") { + if (typeof jsonTypingOptions[id] === "boolean") { + options.enableAutoDiscovery = jsonTypingOptions[id]; + } + else { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Unknown_typing_option_0, id)); + } + } + else if (id === "include") { + options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + } + else if (id === "exclude") { + options.exclude = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + } + else { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Unknown_typing_option_0, id)); + } + } + } + return options; + } } ts.parseJsonConfigFileContent = parseJsonConfigFileContent; function convertCompilerOptionsFromJson(jsonOptions, basePath, configFileName) { @@ -3940,6 +3999,30 @@ var ts; return { options: options, errors: errors }; } ts.convertCompilerOptionsFromJson = convertCompilerOptionsFromJson; + function convertJsonOptionToStringArray(optionName, optionJson, errors, func) { + var items = []; + var invalidOptionType = false; + if (!ts.isArray(optionJson)) { + invalidOptionType = true; + } + else { + for (var _i = 0, _a = optionJson; _i < _a.length; _i++) { + var element = _a[_i]; + if (typeof element === "string") { + var item = func ? func(element) : element; + items.push(item); + } + else { + invalidOptionType = true; + break; + } + } + } + if (invalidOptionType) { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Option_0_should_have_array_of_strings_as_a_value, optionName)); + } + return items; + } })(ts || (ts = {})); var ts; (function (ts) { @@ -4856,6 +4939,9 @@ var ts; } ts.isRequireCall = isRequireCall; function getSpecialPropertyAssignmentKind(expression) { + if (!isInJavaScriptFile(expression)) { + return 0; + } if (expression.kind !== 184) { return 0; } @@ -6620,10 +6706,10 @@ var ts; } } ts.forEachChild = forEachChild; - function createSourceFile(fileName, sourceText, languageVersion, setParentNodes) { + function createSourceFile(fileName, sourceText, languageVersion, setParentNodes, scriptKind) { if (setParentNodes === void 0) { setParentNodes = false; } var start = new Date().getTime(); - var result = Parser.parseSourceFile(fileName, sourceText, languageVersion, undefined, setParentNodes); + var result = Parser.parseSourceFile(fileName, sourceText, languageVersion, undefined, setParentNodes, scriptKind); ts.parseTime += new Date().getTime() - start; return result; } @@ -6657,18 +6743,18 @@ var ts; var parsingContext; var contextFlags; var parseErrorBeforeNextFinishedNode = false; - function parseSourceFile(fileName, _sourceText, languageVersion, _syntaxCursor, setParentNodes) { - var isJavaScriptFile = ts.hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0; - initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); - var result = parseSourceFileWorker(fileName, languageVersion, setParentNodes); + function parseSourceFile(fileName, _sourceText, languageVersion, _syntaxCursor, setParentNodes, scriptKind) { + scriptKind = ts.ensureScriptKind(fileName, scriptKind); + initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind); + var result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind); clearState(); return result; } Parser.parseSourceFile = parseSourceFile; - function getLanguageVariant(fileName) { - return ts.fileExtensionIs(fileName, ".tsx") || ts.fileExtensionIs(fileName, ".jsx") || ts.fileExtensionIs(fileName, ".js") ? 1 : 0; + function getLanguageVariant(scriptKind) { + return scriptKind === 4 || scriptKind === 2 || scriptKind === 1 ? 1 : 0; } - function initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor) { + function initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind) { NodeConstructor = ts.objectAllocator.getNodeConstructor(); SourceFileConstructor = ts.objectAllocator.getSourceFileConstructor(); sourceText = _sourceText; @@ -6678,12 +6764,12 @@ var ts; identifiers = {}; identifierCount = 0; nodeCount = 0; - contextFlags = isJavaScriptFile ? 32 : 0; + contextFlags = scriptKind === 1 || scriptKind === 2 ? 32 : 0; parseErrorBeforeNextFinishedNode = false; scanner.setText(sourceText); scanner.setOnError(scanError); scanner.setScriptTarget(languageVersion); - scanner.setLanguageVariant(getLanguageVariant(fileName)); + scanner.setLanguageVariant(getLanguageVariant(scriptKind)); } function clearState() { scanner.setText(""); @@ -6694,8 +6780,8 @@ var ts; syntaxCursor = undefined; sourceText = undefined; } - function parseSourceFileWorker(fileName, languageVersion, setParentNodes) { - sourceFile = createSourceFile(fileName, languageVersion); + function parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind) { + sourceFile = createSourceFile(fileName, languageVersion, scriptKind); if (contextFlags & 32) { sourceFile.parserContextFlags = 32; } @@ -6744,7 +6830,7 @@ var ts; } } Parser.fixupParentReferences = fixupParentReferences; - function createSourceFile(fileName, languageVersion) { + function createSourceFile(fileName, languageVersion, scriptKind) { var sourceFile = new SourceFileConstructor(251, 0, sourceText.length); nodeCount++; sourceFile.text = sourceText; @@ -6752,7 +6838,8 @@ var ts; sourceFile.languageVersion = languageVersion; sourceFile.fileName = ts.normalizePath(fileName); sourceFile.flags = ts.fileExtensionIs(sourceFile.fileName, ".d.ts") ? 4096 : 0; - sourceFile.languageVariant = getLanguageVariant(sourceFile.fileName); + sourceFile.languageVariant = getLanguageVariant(scriptKind); + sourceFile.scriptKind = scriptKind; return sourceFile; } function setContextFlag(val, flag) { @@ -7288,7 +7375,7 @@ var ts; if (ts.containsParseError(node)) { return undefined; } - var nodeContextFlags = node.parserContextFlags & 31; + var nodeContextFlags = node.parserContextFlags & 63; if (nodeContextFlags !== contextFlags) { return undefined; } @@ -10165,7 +10252,7 @@ var ts; } JSDocParser.isJSDocType = isJSDocType; function parseJSDocTypeExpressionForTests(content, start, length) { - initializeState("file.js", content, 2, true, undefined); + initializeState("file.js", content, 2, undefined, 1); scanner.setText(content, start, length); token = scanner.scan(); var jsDocTypeExpression = parseJSDocTypeExpression(); @@ -10418,7 +10505,7 @@ var ts; } } function parseIsolatedJSDocComment(content, start, length) { - initializeState("file.js", content, 2, true, undefined); + initializeState("file.js", content, 2, undefined, 1); sourceFile = { languageVariant: 0, text: content }; var jsDocComment = parseJSDocCommentWorker(start, length); var diagnostics = parseDiagnostics; @@ -10671,7 +10758,7 @@ var ts; return sourceFile; } if (sourceFile.statements.length === 0) { - return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, undefined, true); + return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, undefined, true, sourceFile.scriptKind); } var incrementalSourceFile = sourceFile; ts.Debug.assert(!incrementalSourceFile.hasBeenIncrementallyParsed); @@ -10685,7 +10772,7 @@ var ts; ts.Debug.assert(ts.textSpanEnd(ts.textChangeRangeNewSpan(changeRange)) === ts.textSpanEnd(ts.textChangeRangeNewSpan(textChangeRange))); var delta = ts.textChangeRangeNewSpan(changeRange).length - changeRange.span.length; updateTokenPositionsAndMarkElements(incrementalSourceFile, changeRange.span.start, ts.textSpanEnd(changeRange.span), ts.textSpanEnd(ts.textChangeRangeNewSpan(changeRange)), delta, oldText, newText, aggressiveChecks); - var result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, true); + var result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, true, sourceFile.scriptKind); return result; } IncrementalParser.updateSourceFile = updateSourceFile; @@ -11440,6 +11527,7 @@ var ts; case 146: case 147: case 153: + case 264: case 154: case 176: case 177: @@ -11930,7 +12018,7 @@ var ts; } function bindModuleExportsAssignment(node) { setCommonJsModuleIndicator(node); - bindExportAssignment(node); + declareSymbol(file.symbol.exports, file.symbol, node, 4 | 7340032 | 512, 0); } function bindThisPropertyAssignment(node) { if (container.kind === 176 || container.kind === 216) { @@ -11952,7 +12040,7 @@ var ts; if (!funcSymbol.members) { funcSymbol.members = {}; } - declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, 4, 107455); + declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, 4, 107455 & ~4); } function bindCallExpression(node) { if (!file.commonJsModuleIndicator && ts.isRequireCall(node, false)) { @@ -12835,7 +12923,9 @@ var ts; function getTargetOfImportClause(node) { var moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier); if (moduleSymbol) { - var exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]); + var exportDefaultSymbol = moduleSymbol.exports["export="] ? + getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : + resolveSymbol(moduleSymbol.exports["default"]); if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, ts.Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); } @@ -12886,8 +12976,14 @@ var ts; if (targetSymbol) { var name_9 = specifier.propertyName || specifier.name; if (name_9.text) { + var symbolFromVariable = void 0; + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { + symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name_9.text); + } + else { + symbolFromVariable = getPropertyOfVariable(targetSymbol, name_9.text); + } var symbolFromModule = getExportOfModule(targetSymbol, name_9.text); - var symbolFromVariable = getPropertyOfVariable(targetSymbol, name_9.text); var symbol = symbolFromModule && symbolFromVariable ? combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; @@ -16348,7 +16444,7 @@ var ts; for (var i = 0; i < checkCount; i++) { var s = i < sourceMax ? getTypeOfSymbol(sourceParams[i]) : getRestTypeOfSignature(source); var t = i < targetMax ? getTypeOfSymbol(targetParams[i]) : getRestTypeOfSignature(target); - var related = compareTypes(t, s, false) || compareTypes(s, t, reportErrors); + var related = compareTypes(s, t, false) || compareTypes(t, s, reportErrors); if (!related) { if (reportErrors) { errorReporter(ts.Diagnostics.Types_of_parameters_0_and_1_are_incompatible, sourceParams[i < sourceMax ? i : sourceMax].name, targetParams[i < targetMax ? i : targetMax].name); @@ -17346,8 +17442,10 @@ var ts; function inferTypes(context, source, target) { var sourceStack; var targetStack; + var maxDepth = 5; var depth = 0; var inferiority = 0; + var visited = {}; inferFromTypes(source, target); function isInProcess(source, target) { for (var i = 0; i < depth; i++) { @@ -17443,9 +17541,17 @@ var ts; if (isInProcess(source, target)) { return; } + if (depth > maxDepth) { + return; + } if (isDeeplyNestedGeneric(source, sourceStack, depth) && isDeeplyNestedGeneric(target, targetStack, depth)) { return; } + var key = source.id + "," + target.id; + if (ts.hasProperty(visited, key)) { + return; + } + visited[key] = true; if (depth === 0) { sourceStack = []; targetStack = []; @@ -33034,7 +33140,7 @@ var ts; ts.ioReadTime = 0; ts.ioWriteTime = 0; var emptyArray = []; - ts.version = "1.8.5"; + ts.version = "1.8.9"; function findConfigFile(searchPath, fileExists) { var fileName = "tsconfig.json"; while (true) { @@ -36696,6 +36802,168 @@ var ts; return name; } ts.stripQuotes = stripQuotes; + function scriptKindIs(fileName, host) { + var scriptKinds = []; + for (var _i = 2; _i < arguments.length; _i++) { + scriptKinds[_i - 2] = arguments[_i]; + } + var scriptKind = getScriptKind(fileName, host); + return ts.forEach(scriptKinds, function (k) { return k === scriptKind; }); + } + ts.scriptKindIs = scriptKindIs; + function getScriptKind(fileName, host) { + var scriptKind = ts.getScriptKindFromFileName(fileName); + if (scriptKind === 0 && host && host.getScriptKind) { + scriptKind = host.getScriptKind(fileName); + } + return ts.ensureScriptKind(fileName, scriptKind); + } + ts.getScriptKind = getScriptKind; +})(ts || (ts = {})); +var ts; +(function (ts) { + var JsTyping; + (function (JsTyping) { + ; + ; + var safeList; + function discoverTypings(host, fileNames, projectRootPath, safeListPath, packageNameToTypingLocation, typingOptions, compilerOptions) { + var inferredTypings = {}; + if (!typingOptions || !typingOptions.enableAutoDiscovery) { + return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; + } + fileNames = ts.filter(ts.map(fileNames, ts.normalizePath), function (f) { return ts.scriptKindIs(f, undefined, 1, 2); }); + if (!safeList) { + var result = ts.readConfigFile(safeListPath, function (path) { return host.readFile(path); }); + if (result.config) { + safeList = result.config; + } + else { + safeList = {}; + } + } + var filesToWatch = []; + var searchDirs = []; + var exclude = []; + mergeTypings(typingOptions.include); + exclude = typingOptions.exclude || []; + var possibleSearchDirs = ts.map(fileNames, ts.getDirectoryPath); + if (projectRootPath !== undefined) { + possibleSearchDirs.push(projectRootPath); + } + searchDirs = ts.deduplicate(possibleSearchDirs); + for (var _i = 0, searchDirs_1 = searchDirs; _i < searchDirs_1.length; _i++) { + var searchDir = searchDirs_1[_i]; + var packageJsonPath = ts.combinePaths(searchDir, "package.json"); + getTypingNamesFromJson(packageJsonPath, filesToWatch); + var bowerJsonPath = ts.combinePaths(searchDir, "bower.json"); + getTypingNamesFromJson(bowerJsonPath, filesToWatch); + var nodeModulesPath = ts.combinePaths(searchDir, "node_modules"); + getTypingNamesFromNodeModuleFolder(nodeModulesPath); + } + getTypingNamesFromSourceFileNames(fileNames); + for (var name_34 in packageNameToTypingLocation) { + if (ts.hasProperty(inferredTypings, name_34) && !inferredTypings[name_34]) { + inferredTypings[name_34] = packageNameToTypingLocation[name_34]; + } + } + for (var _a = 0, exclude_1 = exclude; _a < exclude_1.length; _a++) { + var excludeTypingName = exclude_1[_a]; + delete inferredTypings[excludeTypingName]; + } + var newTypingNames = []; + var cachedTypingPaths = []; + for (var typing in inferredTypings) { + if (inferredTypings[typing] !== undefined) { + cachedTypingPaths.push(inferredTypings[typing]); + } + else { + newTypingNames.push(typing); + } + } + return { cachedTypingPaths: cachedTypingPaths, newTypingNames: newTypingNames, filesToWatch: filesToWatch }; + function mergeTypings(typingNames) { + if (!typingNames) { + return; + } + for (var _i = 0, typingNames_1 = typingNames; _i < typingNames_1.length; _i++) { + var typing = typingNames_1[_i]; + if (!ts.hasProperty(inferredTypings, typing)) { + inferredTypings[typing] = undefined; + } + } + } + function getTypingNamesFromJson(jsonPath, filesToWatch) { + var result = ts.readConfigFile(jsonPath, function (path) { return host.readFile(path); }); + if (result.config) { + var jsonConfig = result.config; + filesToWatch.push(jsonPath); + if (jsonConfig.dependencies) { + mergeTypings(ts.getKeys(jsonConfig.dependencies)); + } + if (jsonConfig.devDependencies) { + mergeTypings(ts.getKeys(jsonConfig.devDependencies)); + } + if (jsonConfig.optionalDependencies) { + mergeTypings(ts.getKeys(jsonConfig.optionalDependencies)); + } + if (jsonConfig.peerDependencies) { + mergeTypings(ts.getKeys(jsonConfig.peerDependencies)); + } + } + } + function getTypingNamesFromSourceFileNames(fileNames) { + var jsFileNames = ts.filter(fileNames, ts.hasJavaScriptFileExtension); + var inferredTypingNames = ts.map(jsFileNames, function (f) { return ts.removeFileExtension(ts.getBaseFileName(f.toLowerCase())); }); + var cleanedTypingNames = ts.map(inferredTypingNames, function (f) { return f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, ""); }); + if (safeList === undefined) { + mergeTypings(cleanedTypingNames); + } + else { + mergeTypings(ts.filter(cleanedTypingNames, function (f) { return ts.hasProperty(safeList, f); })); + } + var hasJsxFile = ts.forEach(fileNames, function (f) { return ts.scriptKindIs(f, undefined, 2); }); + if (hasJsxFile) { + mergeTypings(["react"]); + } + } + function getTypingNamesFromNodeModuleFolder(nodeModulesPath) { + if (!host.directoryExists(nodeModulesPath)) { + return; + } + var typingNames = []; + var fileNames = host.readDirectory(nodeModulesPath, "*.json", undefined, 2); + for (var _i = 0, fileNames_1 = fileNames; _i < fileNames_1.length; _i++) { + var fileName = fileNames_1[_i]; + var normalizedFileName = ts.normalizePath(fileName); + if (ts.getBaseFileName(normalizedFileName) !== "package.json") { + continue; + } + var result = ts.readConfigFile(normalizedFileName, function (path) { return host.readFile(path); }); + if (!result.config) { + continue; + } + var packageJson = result.config; + if (packageJson._requiredBy && + ts.filter(packageJson._requiredBy, function (r) { return r[0] === "#" || r === "/"; }).length === 0) { + continue; + } + if (!packageJson.name) { + continue; + } + if (packageJson.typings) { + var absolutePath = ts.getNormalizedAbsolutePath(packageJson.typings, ts.getDirectoryPath(normalizedFileName)); + inferredTypings[packageJson.name] = absolutePath; + } + else { + typingNames.push(packageJson.name); + } + } + mergeTypings(typingNames); + } + } + JsTyping.discoverTypings = discoverTypings; + })(JsTyping = ts.JsTyping || (ts.JsTyping = {})); })(ts || (ts = {})); var ts; (function (ts) { @@ -37258,9 +37526,9 @@ var ts; } Rules.prototype.getRuleName = function (rule) { var o = this; - for (var name_34 in o) { - if (o[name_34] === rule) { - return name_34; + for (var name_35 in o) { + if (o[name_35] === rule) { + return name_35; } } throw new Error("Unknown rule"); @@ -37854,9 +38122,13 @@ var ts; if (line === 0) { return []; } + var endOfFormatSpan = ts.getEndLinePosition(line, sourceFile); + while (ts.isWhiteSpace(sourceFile.text.charCodeAt(endOfFormatSpan)) && !ts.isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) { + endOfFormatSpan--; + } var span = { pos: ts.getStartPositionOfLine(line - 1, sourceFile), - end: ts.getEndLinePosition(line, sourceFile) + 1 + end: endOfFormatSpan + 1 }; return formatSpan(span, sourceFile, options, rulesProvider, 2); } @@ -38978,7 +39250,7 @@ var ts; })(ts || (ts = {})); var ts; (function (ts) { - ts.servicesVersion = "0.4"; + ts.servicesVersion = "0.5"; var ScriptSnapshot; (function (ScriptSnapshot) { var StringScriptSnapshot = (function () { @@ -39829,7 +40101,8 @@ var ts; entry = { hostFileName: fileName, version: this.host.getScriptVersion(fileName), - scriptSnapshot: scriptSnapshot + scriptSnapshot: scriptSnapshot, + scriptKind: ts.getScriptKind(fileName, this.host) }; } this.fileNameToEntry.set(path, entry); @@ -39876,10 +40149,11 @@ var ts; if (!scriptSnapshot) { throw new Error("Could not find file: '" + fileName + "'."); } + var scriptKind = ts.getScriptKind(fileName, this.host); var version = this.host.getScriptVersion(fileName); var sourceFile; if (this.currentFileName !== fileName) { - sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, 2, version, true); + sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, 2, version, true, scriptKind); } else if (this.currentFileVersion !== version) { var editRange = scriptSnapshot.getChangeRange(this.currentFileScriptSnapshot); @@ -39954,9 +40228,9 @@ var ts; return output.outputText; } ts.transpile = transpile; - function createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, setNodeParents) { + function createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, setNodeParents, scriptKind) { var text = scriptSnapshot.getText(0, scriptSnapshot.getLength()); - var sourceFile = ts.createSourceFile(fileName, text, scriptTarget, setNodeParents); + var sourceFile = ts.createSourceFile(fileName, text, scriptTarget, setNodeParents, scriptKind); setSourceFileFields(sourceFile, scriptSnapshot, version); return sourceFile; } @@ -39997,7 +40271,7 @@ var ts; } } } - return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, true); + return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, true, sourceFile.scriptKind); } ts.updateLanguageServiceSourceFile = updateLanguageServiceSourceFile; function createDocumentRegistry(useCaseSensitiveFileNames, currentDirectory) { @@ -40034,19 +40308,19 @@ var ts; }); return JSON.stringify(bucketInfoArray, undefined, 2); } - function acquireDocument(fileName, compilationSettings, scriptSnapshot, version) { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, true); + function acquireDocument(fileName, compilationSettings, scriptSnapshot, version, scriptKind) { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, true, scriptKind); } - function updateDocument(fileName, compilationSettings, scriptSnapshot, version) { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, false); + function updateDocument(fileName, compilationSettings, scriptSnapshot, version, scriptKind) { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, false, scriptKind); } - function acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, acquiring) { + function acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, acquiring, scriptKind) { var bucket = getBucketForCompilationSettings(compilationSettings, true); var path = ts.toPath(fileName, currentDirectory, getCanonicalFileName); var entry = bucket.get(path); if (!entry) { ts.Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?"); - var sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, false); + var sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, false, scriptKind); entry = { sourceFile: sourceFile, languageServiceRefCount: 0, @@ -40610,10 +40884,11 @@ var ts; if (!changesInCompilationSettingsAffectSyntax) { var oldSourceFile = program && program.getSourceFile(fileName); if (oldSourceFile) { - return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + ts.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); } } - return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } function sourceFileUpToDate(sourceFile) { if (!sourceFile) { @@ -41214,8 +41489,8 @@ var ts; if (element.getStart() <= position && position <= element.getEnd()) { continue; } - var name_35 = element.propertyName || element.name; - exisingImportsOrExports[name_35.text] = true; + var name_36 = element.propertyName || element.name; + exisingImportsOrExports[name_36.text] = true; } if (ts.isEmpty(exisingImportsOrExports)) { return exportsOfModule; @@ -41307,13 +41582,13 @@ var ts; var entries = []; var target = program.getCompilerOptions().target; var nameTable = getNameTable(sourceFile); - for (var name_36 in nameTable) { - if (nameTable[name_36] === position) { + for (var name_37 in nameTable) { + if (nameTable[name_37] === position) { continue; } - if (!uniqueNames[name_36]) { - uniqueNames[name_36] = name_36; - var displayName = getCompletionEntryDisplayName(name_36, target, true); + if (!uniqueNames[name_37]) { + uniqueNames[name_37] = name_37; + var displayName = getCompletionEntryDisplayName(name_37, target, true); if (displayName) { var entry = { name: displayName, @@ -43025,17 +43300,17 @@ var ts; if (isNameOfPropertyAssignment(node)) { var objectLiteral = node.parent.parent; var contextualType = typeChecker.getContextualType(objectLiteral); - var name_37 = node.text; + var name_38 = node.text; if (contextualType) { if (contextualType.flags & 16384) { - var unionProperty = contextualType.getProperty(name_37); + var unionProperty = contextualType.getProperty(name_38); if (unionProperty) { return [unionProperty]; } else { var result_6 = []; ts.forEach(contextualType.types, function (t) { - var symbol = t.getProperty(name_37); + var symbol = t.getProperty(name_38); if (symbol) { result_6.push(symbol); } @@ -43044,7 +43319,7 @@ var ts; } } else { - var symbol_1 = contextualType.getProperty(name_37); + var symbol_1 = contextualType.getProperty(name_38); if (symbol_1) { return [symbol_1]; } @@ -45516,6 +45791,9 @@ var ts; LSHost.prototype.getScriptFileNames = function () { return this.roots.map(function (root) { return root.fileName; }); }; + LSHost.prototype.getScriptKind = function () { + return 0; + }; LSHost.prototype.getScriptVersion = function (filename) { return this.getScriptInfo(filename).svc.latestVersion().toString(); }; @@ -45921,8 +46199,8 @@ var ts; this.inferredProjects = copyListRemovingItem(project, this.inferredProjects); } var fileNames = project.getFileNames(); - for (var _b = 0, fileNames_1 = fileNames; _b < fileNames_1.length; _b++) { - var fileName = fileNames_1[_b]; + for (var _b = 0, fileNames_2 = fileNames; _b < fileNames_2.length; _b++) { + var fileName = fileNames_2[_b]; var info = this.getScriptInfo(fileName); if (info.defaultProject == project) { info.defaultProject = undefined; @@ -46392,6 +46670,7 @@ var ts; else { var defaultOpts = ts.getDefaultCompilerOptions(); defaultOpts.allowNonTsExtensions = true; + defaultOpts.allowJs = true; this.setCompilerOptions(defaultOpts); } this.languageService = ts.createLanguageService(this.host, this.documentRegistry); @@ -47513,6 +47792,14 @@ var ts; var scriptSnapshot = this.shimHost.getScriptSnapshot(fileName); return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot); }; + LanguageServiceShimHostAdapter.prototype.getScriptKind = function (fileName) { + if ("getScriptKind" in this.shimHost) { + return this.shimHost.getScriptKind(fileName); + } + else { + return 0; + } + }; LanguageServiceShimHostAdapter.prototype.getScriptVersion = function (fileName) { return this.shimHost.getScriptVersion(fileName); }; @@ -47566,8 +47853,14 @@ var ts; this.directoryExists = function (directoryName) { return _this.shimHost.directoryExists(directoryName); }; } } - CoreServicesShimHostAdapter.prototype.readDirectory = function (rootDir, extension, exclude) { - var encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)); + CoreServicesShimHostAdapter.prototype.readDirectory = function (rootDir, extension, exclude, depth) { + var encoded; + try { + encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude), depth); + } + catch (e) { + encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)); + } return JSON.parse(encoded); }; CoreServicesShimHostAdapter.prototype.fileExists = function (fileName) { @@ -47912,6 +48205,7 @@ var ts; if (result.error) { return { options: {}, + typingOptions: {}, files: [], errors: [realizeDiagnostic(result.error, "\r\n")] }; @@ -47920,6 +48214,7 @@ var ts; var configFile = ts.parseJsonConfigFileContent(result.config, _this.host, ts.getDirectoryPath(normalizedFileName), {}, normalizedFileName); return { options: configFile.options, + typingOptions: configFile.typingOptions, files: configFile.fileNames, errors: realizeDiagnostics(configFile.errors, "\r\n") }; @@ -47928,6 +48223,14 @@ var ts; CoreServicesShimObject.prototype.getDefaultCompilationSettings = function () { return this.forwardJSONCall("getDefaultCompilationSettings()", function () { return ts.getDefaultCompilerOptions(); }); }; + CoreServicesShimObject.prototype.discoverTypings = function (discoverTypingsJson) { + var _this = this; + var getCanonicalFileName = ts.createGetCanonicalFileName(false); + return this.forwardJSONCall("discoverTypings()", function () { + var info = JSON.parse(discoverTypingsJson); + return ts.JsTyping.discoverTypings(_this.host, info.fileNames, ts.toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName), ts.toPath(info.safeListPath, info.safeListPath, getCanonicalFileName), info.packageNameToTypingLocation, info.typingOptions, info.compilerOptions); + }); + }; return CoreServicesShimObject; }(ShimBase)); var TypeScriptServicesFactory = (function () { diff --git a/lib/typescript.d.ts b/lib/typescript.d.ts index dd32085e5c1..2c1d5e609fb 100644 --- a/lib/typescript.d.ts +++ b/lib/typescript.d.ts @@ -1480,6 +1480,20 @@ declare namespace ts { noImplicitUseStrict?: boolean; [option: string]: string | number | boolean; } + interface TypingOptions { + enableAutoDiscovery?: boolean; + include?: string[]; + exclude?: string[]; + [option: string]: string[] | boolean; + } + interface DiscoverTypingsInfo { + fileNames: string[]; + projectRootPath: string; + safeListPath: string; + packageNameToTypingLocation: Map; + typingOptions: TypingOptions; + compilerOptions: CompilerOptions; + } enum ModuleKind { None = 0, CommonJS = 1, @@ -1502,6 +1516,13 @@ declare namespace ts { line: number; character: number; } + enum ScriptKind { + Unknown = 0, + JS = 1, + JSX = 2, + TS = 3, + TSX = 4, + } enum ScriptTarget { ES3 = 0, ES5 = 1, @@ -1515,6 +1536,7 @@ declare namespace ts { } interface ParsedCommandLine { options: CompilerOptions; + typingOptions?: TypingOptions; fileNames: string[]; errors: Diagnostic[]; } @@ -1663,7 +1685,7 @@ declare namespace ts { declare namespace ts { function createNode(kind: SyntaxKind, pos?: number, end?: number): Node; function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T; - function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes?: boolean): SourceFile; + function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile; function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; } declare namespace ts { @@ -1798,6 +1820,7 @@ declare namespace ts { getNewLine?(): string; getProjectVersion?(): string; getScriptFileNames(): string[]; + getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot; getLocalizedDiagnosticMessages?(): any; @@ -2166,7 +2189,7 @@ declare namespace ts { * @parm version Current version of the file. Only used if the file was not found * in the registry and a new one was created. */ - acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile; + acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile; /** * Request an updated version of an already existing SourceFile with a given fileName * and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile @@ -2179,7 +2202,7 @@ declare namespace ts { * @param scriptSnapshot Text of the file. * @param version Current version of the file. */ - updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile; + updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile; /** * Informs the DocumentRegistry that a file is not needed any longer. * @@ -2303,7 +2326,7 @@ declare namespace ts { } function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput; function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string; - function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile; + function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile; let disableIncrementalParsing: boolean; function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory?: string): DocumentRegistry; diff --git a/lib/typescript.js b/lib/typescript.js index 5923ee0e111..a952502f6ed 100644 --- a/lib/typescript.js +++ b/lib/typescript.js @@ -409,7 +409,7 @@ var ts; // its type can be specified usign a JSDoc comment. ParserContextFlags[ParserContextFlags["JavaScriptFile"] = 32] = "JavaScriptFile"; // Context flags set directly by the parser. - ParserContextFlags[ParserContextFlags["ParserGeneratedFlags"] = 31] = "ParserGeneratedFlags"; + ParserContextFlags[ParserContextFlags["ParserGeneratedFlags"] = 63] = "ParserGeneratedFlags"; // Exclude these flags when parsing a Type ParserContextFlags[ParserContextFlags["TypeExcludesFlags"] = 10] = "TypeExcludesFlags"; // Context flags computed by aggregating child flags upwards. @@ -716,6 +716,14 @@ var ts; NewLineKind[NewLineKind["LineFeed"] = 1] = "LineFeed"; })(ts.NewLineKind || (ts.NewLineKind = {})); var NewLineKind = ts.NewLineKind; + (function (ScriptKind) { + ScriptKind[ScriptKind["Unknown"] = 0] = "Unknown"; + ScriptKind[ScriptKind["JS"] = 1] = "JS"; + ScriptKind[ScriptKind["JSX"] = 2] = "JSX"; + ScriptKind[ScriptKind["TS"] = 3] = "TS"; + ScriptKind[ScriptKind["TSX"] = 4] = "TSX"; + })(ts.ScriptKind || (ts.ScriptKind = {})); + var ScriptKind = ts.ScriptKind; (function (ScriptTarget) { ScriptTarget[ScriptTarget["ES3"] = 0] = "ES3"; ScriptTarget[ScriptTarget["ES5"] = 1] = "ES5"; @@ -1137,6 +1145,14 @@ var ts; return hasOwnProperty.call(map, key); } ts.hasProperty = hasProperty; + function getKeys(map) { + var keys = []; + for (var key in map) { + keys.push(key); + } + return keys; + } + ts.getKeys = getKeys; function getProperty(map, key) { return hasOwnProperty.call(map, key) ? map[key] : undefined; } @@ -1594,6 +1610,32 @@ var ts; return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; } ts.fileExtensionIs = fileExtensionIs; + function ensureScriptKind(fileName, scriptKind) { + // 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 we attempt + // to get the ScriptKind from the file name. If it cannot be resolved + // from the file name then the default 'TS' script kind is returned. + return (scriptKind || getScriptKindFromFileName(fileName)) || 3 /* TS */; + } + ts.ensureScriptKind = ensureScriptKind; + function getScriptKindFromFileName(fileName) { + var ext = fileName.substr(fileName.lastIndexOf(".")); + switch (ext.toLowerCase()) { + case ".js": + return 1 /* JS */; + case ".jsx": + return 2 /* JSX */; + case ".ts": + return 3 /* TS */; + case ".tsx": + return 4 /* TSX */; + default: + return 0 /* Unknown */; + } + } + ts.getScriptKindFromFileName = getScriptKindFromFileName; /** * List of supported extensions in order of file resolution precedence. */ @@ -2819,6 +2861,7 @@ var ts; Only_amd_and_system_modules_are_supported_alongside_0: { code: 6082, category: ts.DiagnosticCategory.Error, key: "Only_amd_and_system_modules_are_supported_alongside_0_6082", message: "Only 'amd' and 'system' modules are supported alongside --{0}." }, Allow_javascript_files_to_be_compiled: { code: 6083, category: ts.DiagnosticCategory.Message, key: "Allow_javascript_files_to_be_compiled_6083", message: "Allow javascript files to be compiled." }, Specifies_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit: { code: 6084, category: ts.DiagnosticCategory.Message, key: "Specifies_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit_6084", message: "Specifies the object invoked for createElement and __spread when targeting 'react' JSX emit" }, + Option_0_should_have_array_of_strings_as_a_value: { code: 6103, category: ts.DiagnosticCategory.Error, key: "Option_0_should_have_array_of_strings_as_a_value_6103", message: "Option '{0}' should have array of strings as a value." }, Do_not_emit_use_strict_directives_in_module_output: { code: 6112, category: ts.DiagnosticCategory.Message, key: "Do_not_emit_use_strict_directives_in_module_output_6112", message: "Do not emit 'use strict' directives in module output." }, Variable_0_implicitly_has_an_1_type: { code: 7005, category: ts.DiagnosticCategory.Error, key: "Variable_0_implicitly_has_an_1_type_7005", message: "Variable '{0}' implicitly has an '{1}' type." }, Parameter_0_implicitly_has_an_1_type: { code: 7006, category: ts.DiagnosticCategory.Error, key: "Parameter_0_implicitly_has_an_1_type_7006", message: "Parameter '{0}' implicitly has an '{1}' type." }, @@ -2869,7 +2912,8 @@ var ts; An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17006, category: ts.DiagnosticCategory.Error, key: "An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_ex_17006", message: "An unary expression with the '{0}' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17007, category: ts.DiagnosticCategory.Error, key: "A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Con_17007", message: "A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, JSX_element_0_has_no_corresponding_closing_tag: { code: 17008, category: ts.DiagnosticCategory.Error, key: "JSX_element_0_has_no_corresponding_closing_tag_17008", message: "JSX element '{0}' has no corresponding closing tag." }, - super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class: { code: 17009, category: ts.DiagnosticCategory.Error, key: "super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class_17009", message: "'super' must be called before accessing 'this' in the constructor of a derived class." } + super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class: { code: 17009, category: ts.DiagnosticCategory.Error, key: "super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class_17009", message: "'super' must be called before accessing 'this' in the constructor of a derived class." }, + Unknown_typing_option_0: { code: 17010, category: ts.DiagnosticCategory.Error, key: "Unknown_typing_option_0_17010", message: "Unknown typing option '{0}'." } }; })(ts || (ts = {})); /// @@ -5548,6 +5592,9 @@ var ts; /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property /// assignments we treat as special in the binder function getSpecialPropertyAssignmentKind(expression) { + if (!isInJavaScriptFile(expression)) { + return 0 /* None */; + } if (expression.kind !== 184 /* BinaryExpression */) { return 0 /* None */; } @@ -7557,10 +7604,10 @@ var ts; } } ts.forEachChild = forEachChild; - function createSourceFile(fileName, sourceText, languageVersion, setParentNodes) { + function createSourceFile(fileName, sourceText, languageVersion, setParentNodes, scriptKind) { if (setParentNodes === void 0) { setParentNodes = false; } var start = new Date().getTime(); - var result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes); + var result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); ts.parseTime += new Date().getTime() - start; return result; } @@ -7685,19 +7732,19 @@ var ts; // Note: any errors at the end of the file that do not precede a regular node, should get // attached to the EOF token. var parseErrorBeforeNextFinishedNode = false; - function parseSourceFile(fileName, _sourceText, languageVersion, _syntaxCursor, setParentNodes) { - var isJavaScriptFile = ts.hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0; - initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); - var result = parseSourceFileWorker(fileName, languageVersion, setParentNodes); + function parseSourceFile(fileName, _sourceText, languageVersion, _syntaxCursor, setParentNodes, scriptKind) { + scriptKind = ts.ensureScriptKind(fileName, scriptKind); + initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind); + var result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind); clearState(); return result; } Parser.parseSourceFile = parseSourceFile; - function getLanguageVariant(fileName) { + function getLanguageVariant(scriptKind) { // .tsx and .jsx files are treated as jsx language variant. - return ts.fileExtensionIs(fileName, ".tsx") || ts.fileExtensionIs(fileName, ".jsx") || ts.fileExtensionIs(fileName, ".js") ? 1 /* JSX */ : 0 /* Standard */; + return scriptKind === 4 /* TSX */ || scriptKind === 2 /* JSX */ || scriptKind === 1 /* JS */ ? 1 /* JSX */ : 0 /* Standard */; } - function initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor) { + function initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind) { NodeConstructor = ts.objectAllocator.getNodeConstructor(); SourceFileConstructor = ts.objectAllocator.getSourceFileConstructor(); sourceText = _sourceText; @@ -7707,13 +7754,13 @@ var ts; identifiers = {}; identifierCount = 0; nodeCount = 0; - contextFlags = isJavaScriptFile ? 32 /* JavaScriptFile */ : 0 /* None */; + contextFlags = scriptKind === 1 /* JS */ || scriptKind === 2 /* JSX */ ? 32 /* JavaScriptFile */ : 0 /* 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() { // Clear out the text the scanner is pointing at, so it doesn't keep anything alive unnecessarily. @@ -7726,8 +7773,8 @@ var ts; syntaxCursor = undefined; sourceText = undefined; } - function parseSourceFileWorker(fileName, languageVersion, setParentNodes) { - sourceFile = createSourceFile(fileName, languageVersion); + function parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind) { + sourceFile = createSourceFile(fileName, languageVersion, scriptKind); if (contextFlags & 32 /* JavaScriptFile */) { sourceFile.parserContextFlags = 32 /* JavaScriptFile */; } @@ -7784,7 +7831,7 @@ var ts; } } Parser.fixupParentReferences = fixupParentReferences; - function createSourceFile(fileName, languageVersion) { + function createSourceFile(fileName, languageVersion, scriptKind) { // 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 var sourceFile = new SourceFileConstructor(251 /* SourceFile */, /*pos*/ 0, /* end */ sourceText.length); @@ -7794,7 +7841,8 @@ var ts; sourceFile.languageVersion = languageVersion; sourceFile.fileName = ts.normalizePath(fileName); sourceFile.flags = ts.fileExtensionIs(sourceFile.fileName, ".d.ts") ? 4096 /* DeclarationFile */ : 0; - sourceFile.languageVariant = getLanguageVariant(sourceFile.fileName); + sourceFile.languageVariant = getLanguageVariant(scriptKind); + sourceFile.scriptKind = scriptKind; return sourceFile; } function setContextFlag(val, flag) { @@ -8460,7 +8508,7 @@ var ts; // differently depending on what mode it is in. // // This also applies to all our other context flags as well. - var nodeContextFlags = node.parserContextFlags & 31 /* ParserGeneratedFlags */; + var nodeContextFlags = node.parserContextFlags & 63 /* ParserGeneratedFlags */; if (nodeContextFlags !== contextFlags) { return undefined; } @@ -12100,7 +12148,7 @@ var ts; } JSDocParser.isJSDocType = isJSDocType; function parseJSDocTypeExpressionForTests(content, start, length) { - initializeState("file.js", content, 2 /* Latest */, /*isJavaScriptFile*/ true, /*_syntaxCursor:*/ undefined); + initializeState("file.js", content, 2 /* Latest */, /*_syntaxCursor:*/ undefined, 1 /* JS */); scanner.setText(content, start, length); token = scanner.scan(); var jsDocTypeExpression = parseJSDocTypeExpression(); @@ -12367,7 +12415,7 @@ var ts; } } function parseIsolatedJSDocComment(content, start, length) { - initializeState("file.js", content, 2 /* Latest */, /*isJavaScriptFile*/ true, /*_syntaxCursor:*/ undefined); + initializeState("file.js", content, 2 /* Latest */, /*_syntaxCursor:*/ undefined, 1 /* JS */); sourceFile = { languageVariant: 0 /* Standard */, text: content }; var jsDocComment = parseJSDocCommentWorker(start, length); var diagnostics = parseDiagnostics; @@ -12637,7 +12685,7 @@ var ts; if (sourceFile.statements.length === 0) { // If we don't have any statements in the current source file, then there's no real // way to incrementally parse. So just do a full parse instead. - return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true); + return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, sourceFile.scriptKind); } // Make sure we're not trying to incrementally update a source file more than once. Once // we do an update the original source file is considered unusbale from that point onwards. @@ -12693,7 +12741,7 @@ var ts; // inconsistent tree. Setting the parents on the new tree should be very fast. We // will immediately bail out of walking any subtrees when we can see that their parents // are already correct. - var result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true); + var result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind); return result; } IncrementalParser.updateSourceFile = updateSourceFile; @@ -13745,6 +13793,7 @@ var ts; case 146 /* GetAccessor */: case 147 /* SetAccessor */: case 153 /* FunctionType */: + case 264 /* JSDocFunctionType */: case 154 /* ConstructorType */: case 176 /* FunctionExpression */: case 177 /* ArrowFunction */: @@ -14350,7 +14399,7 @@ var ts; function bindModuleExportsAssignment(node) { // 'module.exports = expr' assignment setCommonJsModuleIndicator(node); - bindExportAssignment(node); + declareSymbol(file.symbol.exports, file.symbol, node, 4 /* Property */ | 7340032 /* Export */ | 512 /* ValueModule */, 0 /* None */); } function bindThisPropertyAssignment(node) { // Declare a 'member' in case it turns out the container was an ES5 class @@ -14380,7 +14429,8 @@ var ts; funcSymbol.members = {}; } // Declare the method/property - declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, 4 /* Property */, 107455 /* PropertyExcludes */); + // It's acceptable for multiple prototype property assignments of the same identifier to occur + declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, 4 /* Property */, 107455 /* PropertyExcludes */ & ~4 /* Property */); } function bindCallExpression(node) { // We're only inspecting call expressions to detect CommonJS modules, so we can skip @@ -15439,7 +15489,9 @@ var ts; function getTargetOfImportClause(node) { var moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier); if (moduleSymbol) { - var exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]); + var exportDefaultSymbol = moduleSymbol.exports["export="] ? + getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : + resolveSymbol(moduleSymbol.exports["default"]); if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, ts.Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); } @@ -15508,8 +15560,15 @@ var ts; if (targetSymbol) { var name_9 = specifier.propertyName || specifier.name; if (name_9.text) { + var symbolFromVariable = void 0; + // First check if module was specified with "export=". If so, get the member from the resolved type + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { + symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name_9.text); + } + else { + symbolFromVariable = getPropertyOfVariable(targetSymbol, name_9.text); + } var symbolFromModule = getExportOfModule(targetSymbol, name_9.text); - var symbolFromVariable = getPropertyOfVariable(targetSymbol, name_9.text); var symbol = symbolFromModule && symbolFromVariable ? combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; @@ -19403,7 +19462,7 @@ var ts; for (var i = 0; i < checkCount; i++) { var s = i < sourceMax ? getTypeOfSymbol(sourceParams[i]) : getRestTypeOfSignature(source); var t = i < targetMax ? getTypeOfSymbol(targetParams[i]) : getRestTypeOfSignature(target); - var related = compareTypes(t, s, /*reportErrors*/ false) || compareTypes(s, t, reportErrors); + var related = compareTypes(s, t, /*reportErrors*/ false) || compareTypes(t, s, reportErrors); if (!related) { if (reportErrors) { errorReporter(ts.Diagnostics.Types_of_parameters_0_and_1_are_incompatible, sourceParams[i < sourceMax ? i : sourceMax].name, targetParams[i < targetMax ? i : targetMax].name); @@ -20528,8 +20587,10 @@ var ts; function inferTypes(context, source, target) { var sourceStack; var targetStack; + var maxDepth = 5; var depth = 0; var inferiority = 0; + var visited = {}; inferFromTypes(source, target); function isInProcess(source, target) { for (var i = 0; i < depth; i++) { @@ -20655,9 +20716,19 @@ var ts; if (isInProcess(source, target)) { return; } + // we delibirately limit the depth we examine to infer types: this speeds up the overall inference process + // and user rarely expects inferences to be made from the deeply nested constituents. + if (depth > maxDepth) { + return; + } if (isDeeplyNestedGeneric(source, sourceStack, depth) && isDeeplyNestedGeneric(target, targetStack, depth)) { return; } + var key = source.id + "," + target.id; + if (ts.hasProperty(visited, key)) { + return; + } + visited[key] = true; if (depth === 0) { sourceStack = []; targetStack = []; @@ -39261,7 +39332,7 @@ var ts; /* @internal */ ts.ioWriteTime = 0; /** The version of the TypeScript compiler release */ var emptyArray = []; - ts.version = "1.8.5"; + ts.version = "1.8.9"; function findConfigFile(searchPath, fileExists) { var fileName = "tsconfig.json"; while (true) { @@ -40972,6 +41043,7 @@ var ts; return { options: options, fileNames: getFileNames(), + typingOptions: getTypingOptions(), errors: errors }; function getFileNames() { @@ -41031,6 +41103,34 @@ var ts; } return fileNames; } + function getTypingOptions() { + var options = ts.getBaseFileName(configFileName) === "jsconfig.json" + ? { enableAutoDiscovery: true, include: [], exclude: [] } + : { enableAutoDiscovery: false, include: [], exclude: [] }; + var jsonTypingOptions = json["typingOptions"]; + if (jsonTypingOptions) { + for (var id in jsonTypingOptions) { + if (id === "enableAutoDiscovery") { + if (typeof jsonTypingOptions[id] === "boolean") { + options.enableAutoDiscovery = jsonTypingOptions[id]; + } + else { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Unknown_typing_option_0, id)); + } + } + else if (id === "include") { + options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + } + else if (id === "exclude") { + options.exclude = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + } + else { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Unknown_typing_option_0, id)); + } + } + } + return options; + } } ts.parseJsonConfigFileContent = parseJsonConfigFileContent; function convertCompilerOptionsFromJson(jsonOptions, basePath, configFileName) { @@ -41079,6 +41179,30 @@ var ts; return { options: options, errors: errors }; } ts.convertCompilerOptionsFromJson = convertCompilerOptionsFromJson; + function convertJsonOptionToStringArray(optionName, optionJson, errors, func) { + var items = []; + var invalidOptionType = false; + if (!ts.isArray(optionJson)) { + invalidOptionType = true; + } + else { + for (var _i = 0, _a = optionJson; _i < _a.length; _i++) { + var element = _a[_i]; + if (typeof element === "string") { + var item = func ? func(element) : element; + items.push(item); + } + else { + invalidOptionType = true; + break; + } + } + } + if (invalidOptionType) { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Option_0_should_have_array_of_strings_as_a_value, optionName)); + } + return items; + } })(ts || (ts = {})); /* @internal */ var ts; @@ -43742,6 +43866,212 @@ var ts; return name; } ts.stripQuotes = stripQuotes; + function scriptKindIs(fileName, host) { + var scriptKinds = []; + for (var _i = 2; _i < arguments.length; _i++) { + scriptKinds[_i - 2] = arguments[_i]; + } + var scriptKind = getScriptKind(fileName, host); + return ts.forEach(scriptKinds, function (k) { return k === scriptKind; }); + } + ts.scriptKindIs = scriptKindIs; + function getScriptKind(fileName, host) { + // First check to see if the script kind can be determined from the file name + var scriptKind = ts.getScriptKindFromFileName(fileName); + if (scriptKind === 0 /* Unknown */ && host && host.getScriptKind) { + // Next check to see if the host can resolve the script kind + scriptKind = host.getScriptKind(fileName); + } + return ts.ensureScriptKind(fileName, scriptKind); + } + ts.getScriptKind = getScriptKind; +})(ts || (ts = {})); +// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. +// See LICENSE.txt in the project root for complete license information. +/// +/* @internal */ +var ts; +(function (ts) { + var JsTyping; + (function (JsTyping) { + ; + ; + // A map of loose file names to library names + // that we are confident require typings + var safeList; + /** + * @param host is the object providing I/O related operations. + * @param fileNames are the file names that belong to the same project + * @param projectRootPath is the path to the project root directory + * @param safeListPath is the path used to retrieve the safe list + * @param packageNameToTypingLocation is the map of package names to their cached typing locations + * @param typingOptions are used to customize the typing inference process + * @param compilerOptions are used as a source for typing inference + */ + function discoverTypings(host, fileNames, projectRootPath, safeListPath, packageNameToTypingLocation, typingOptions, compilerOptions) { + // A typing name to typing file path mapping + var inferredTypings = {}; + if (!typingOptions || !typingOptions.enableAutoDiscovery) { + return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; + } + // Only infer typings for .js and .jsx files + fileNames = ts.filter(ts.map(fileNames, ts.normalizePath), function (f) { return ts.scriptKindIs(f, /*LanguageServiceHost*/ undefined, 1 /* JS */, 2 /* JSX */); }); + if (!safeList) { + var result = ts.readConfigFile(safeListPath, function (path) { return host.readFile(path); }); + if (result.config) { + safeList = result.config; + } + else { + safeList = {}; + } + } + var filesToWatch = []; + // Directories to search for package.json, bower.json and other typing information + var searchDirs = []; + var exclude = []; + mergeTypings(typingOptions.include); + exclude = typingOptions.exclude || []; + var possibleSearchDirs = ts.map(fileNames, ts.getDirectoryPath); + if (projectRootPath !== undefined) { + possibleSearchDirs.push(projectRootPath); + } + searchDirs = ts.deduplicate(possibleSearchDirs); + for (var _i = 0, searchDirs_1 = searchDirs; _i < searchDirs_1.length; _i++) { + var searchDir = searchDirs_1[_i]; + var packageJsonPath = ts.combinePaths(searchDir, "package.json"); + getTypingNamesFromJson(packageJsonPath, filesToWatch); + var bowerJsonPath = ts.combinePaths(searchDir, "bower.json"); + getTypingNamesFromJson(bowerJsonPath, filesToWatch); + var nodeModulesPath = ts.combinePaths(searchDir, "node_modules"); + getTypingNamesFromNodeModuleFolder(nodeModulesPath); + } + getTypingNamesFromSourceFileNames(fileNames); + // Add the cached typing locations for inferred typings that are already installed + for (var name_34 in packageNameToTypingLocation) { + if (ts.hasProperty(inferredTypings, name_34) && !inferredTypings[name_34]) { + inferredTypings[name_34] = packageNameToTypingLocation[name_34]; + } + } + // Remove typings that the user has added to the exclude list + for (var _a = 0, exclude_1 = exclude; _a < exclude_1.length; _a++) { + var excludeTypingName = exclude_1[_a]; + delete inferredTypings[excludeTypingName]; + } + var newTypingNames = []; + var cachedTypingPaths = []; + for (var typing in inferredTypings) { + if (inferredTypings[typing] !== undefined) { + cachedTypingPaths.push(inferredTypings[typing]); + } + else { + newTypingNames.push(typing); + } + } + return { cachedTypingPaths: cachedTypingPaths, newTypingNames: newTypingNames, filesToWatch: filesToWatch }; + /** + * Merge a given list of typingNames to the inferredTypings map + */ + function mergeTypings(typingNames) { + if (!typingNames) { + return; + } + for (var _i = 0, typingNames_1 = typingNames; _i < typingNames_1.length; _i++) { + var typing = typingNames_1[_i]; + if (!ts.hasProperty(inferredTypings, typing)) { + inferredTypings[typing] = undefined; + } + } + } + /** + * Get the typing info from common package manager json files like package.json or bower.json + */ + function getTypingNamesFromJson(jsonPath, filesToWatch) { + var result = ts.readConfigFile(jsonPath, function (path) { return host.readFile(path); }); + if (result.config) { + var jsonConfig = result.config; + filesToWatch.push(jsonPath); + if (jsonConfig.dependencies) { + mergeTypings(ts.getKeys(jsonConfig.dependencies)); + } + if (jsonConfig.devDependencies) { + mergeTypings(ts.getKeys(jsonConfig.devDependencies)); + } + if (jsonConfig.optionalDependencies) { + mergeTypings(ts.getKeys(jsonConfig.optionalDependencies)); + } + if (jsonConfig.peerDependencies) { + mergeTypings(ts.getKeys(jsonConfig.peerDependencies)); + } + } + } + /** + * Infer typing names from given file names. For example, the file name "jquery-min.2.3.4.js" + * should be inferred to the 'jquery' typing name; and "angular-route.1.2.3.js" should be inferred + * to the 'angular-route' typing name. + * @param fileNames are the names for source files in the project + */ + function getTypingNamesFromSourceFileNames(fileNames) { + var jsFileNames = ts.filter(fileNames, ts.hasJavaScriptFileExtension); + var inferredTypingNames = ts.map(jsFileNames, function (f) { return ts.removeFileExtension(ts.getBaseFileName(f.toLowerCase())); }); + var cleanedTypingNames = ts.map(inferredTypingNames, function (f) { return f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, ""); }); + if (safeList === undefined) { + mergeTypings(cleanedTypingNames); + } + else { + mergeTypings(ts.filter(cleanedTypingNames, function (f) { return ts.hasProperty(safeList, f); })); + } + var hasJsxFile = ts.forEach(fileNames, function (f) { return ts.scriptKindIs(f, /*LanguageServiceHost*/ undefined, 2 /* JSX */); }); + if (hasJsxFile) { + mergeTypings(["react"]); + } + } + /** + * Infer typing names from node_module folder + * @param nodeModulesPath is the path to the "node_modules" folder + */ + function getTypingNamesFromNodeModuleFolder(nodeModulesPath) { + // Todo: add support for ModuleResolutionHost too + if (!host.directoryExists(nodeModulesPath)) { + return; + } + var typingNames = []; + var fileNames = host.readDirectory(nodeModulesPath, "*.json", /*exclude*/ undefined, /*depth*/ 2); + for (var _i = 0, fileNames_1 = fileNames; _i < fileNames_1.length; _i++) { + var fileName = fileNames_1[_i]; + var normalizedFileName = ts.normalizePath(fileName); + if (ts.getBaseFileName(normalizedFileName) !== "package.json") { + continue; + } + var result = ts.readConfigFile(normalizedFileName, function (path) { return host.readFile(path); }); + if (!result.config) { + continue; + } + var packageJson = result.config; + // npm 3's package.json contains a "_requiredBy" field + // we should include all the top level module names for npm 2, and only module names whose + // "_requiredBy" field starts with "#" or equals "/" for npm 3. + if (packageJson._requiredBy && + ts.filter(packageJson._requiredBy, function (r) { return r[0] === "#" || r === "/"; }).length === 0) { + continue; + } + // If the package has its own d.ts typings, those will take precedence. Otherwise the package name will be used + // to download d.ts files from DefinitelyTyped + if (!packageJson.name) { + continue; + } + if (packageJson.typings) { + var absolutePath = ts.getNormalizedAbsolutePath(packageJson.typings, ts.getDirectoryPath(normalizedFileName)); + inferredTypings[packageJson.name] = absolutePath; + } + else { + typingNames.push(packageJson.name); + } + } + mergeTypings(typingNames); + } + } + JsTyping.discoverTypings = discoverTypings; + })(JsTyping = ts.JsTyping || (ts.JsTyping = {})); })(ts || (ts = {})); /// /// @@ -44452,9 +44782,9 @@ var ts; } Rules.prototype.getRuleName = function (rule) { var o = this; - for (var name_34 in o) { - if (o[name_34] === rule) { - return name_34; + for (var name_35 in o) { + if (o[name_35] === rule) { + return name_35; } } throw new Error("Unknown rule"); @@ -45129,12 +45459,20 @@ var ts; if (line === 0) { return []; } - // get the span for the previous\current line + // After the enter key, the cursor is now at a new line. The new line may or may not contain non-whitespace characters. + // If the new line has only whitespaces, we won't want to format this line, because that would remove the indentation as + // trailing whitespaces. So the end of the formatting span should be the later one between: + // 1. the end of the previous line + // 2. the last non-whitespace character in the current line + var endOfFormatSpan = ts.getEndLinePosition(line, sourceFile); + while (ts.isWhiteSpace(sourceFile.text.charCodeAt(endOfFormatSpan)) && !ts.isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) { + endOfFormatSpan--; + } var span = { // get start position for the previous line pos: ts.getStartPositionOfLine(line - 1, sourceFile), - // get end position for the current line (end value is exclusive so add 1 to the result) - end: ts.getEndLinePosition(line, sourceFile) + 1 + // end value is exclusive so add 1 to the result + end: endOfFormatSpan + 1 }; return formatSpan(span, sourceFile, options, rulesProvider, 2 /* FormatOnEnter */); } @@ -46456,12 +46794,13 @@ var ts; /// /// /// +/// /// /// var ts; (function (ts) { /** The version of the language service API */ - ts.servicesVersion = "0.4"; + ts.servicesVersion = "0.5"; var ScriptSnapshot; (function (ScriptSnapshot) { var StringScriptSnapshot = (function () { @@ -47449,7 +47788,8 @@ var ts; entry = { hostFileName: fileName, version: this.host.getScriptVersion(fileName), - scriptSnapshot: scriptSnapshot + scriptSnapshot: scriptSnapshot, + scriptKind: ts.getScriptKind(fileName, this.host) }; } this.fileNameToEntry.set(path, entry); @@ -47497,11 +47837,12 @@ var ts; // The host does not know about this file. throw new Error("Could not find file: '" + fileName + "'."); } + var scriptKind = ts.getScriptKind(fileName, this.host); var version = this.host.getScriptVersion(fileName); var sourceFile; if (this.currentFileName !== fileName) { // This is a new file, just parse it - sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, 2 /* Latest */, version, /*setNodeParents*/ true); + sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, 2 /* Latest */, version, /*setNodeParents*/ true, scriptKind); } else if (this.currentFileVersion !== version) { // This is the same file, just a newer version. Incrementally parse the file. @@ -47601,9 +47942,9 @@ var ts; return output.outputText; } ts.transpile = transpile; - function createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, setNodeParents) { + function createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, setNodeParents, scriptKind) { var text = scriptSnapshot.getText(0, scriptSnapshot.getLength()); - var sourceFile = ts.createSourceFile(fileName, text, scriptTarget, setNodeParents); + var sourceFile = ts.createSourceFile(fileName, text, scriptTarget, setNodeParents, scriptKind); setSourceFileFields(sourceFile, scriptSnapshot, version); return sourceFile; } @@ -47656,7 +47997,7 @@ var ts; } } // Otherwise, just create a new source file. - return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true); + return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true, sourceFile.scriptKind); } ts.updateLanguageServiceSourceFile = updateLanguageServiceSourceFile; function createDocumentRegistry(useCaseSensitiveFileNames, currentDirectory) { @@ -47695,20 +48036,20 @@ var ts; }); return JSON.stringify(bucketInfoArray, undefined, 2); } - function acquireDocument(fileName, compilationSettings, scriptSnapshot, version) { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true); + function acquireDocument(fileName, compilationSettings, scriptSnapshot, version, scriptKind) { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true, scriptKind); } - function updateDocument(fileName, compilationSettings, scriptSnapshot, version) { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false); + function updateDocument(fileName, compilationSettings, scriptSnapshot, version, scriptKind) { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false, scriptKind); } - function acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, acquiring) { + function acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, acquiring, scriptKind) { var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true); var path = ts.toPath(fileName, currentDirectory, getCanonicalFileName); var entry = bucket.get(path); if (!entry) { ts.Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?"); // Have never seen this file with these settings. Create a new source file for it. - var sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false); + var sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind); entry = { sourceFile: sourceFile, languageServiceRefCount: 0, @@ -48396,11 +48737,15 @@ var 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. - return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + // 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" + ts.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); } } // Could not find this file in the old program, create a new SourceFile for it. - return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } function sourceFileUpToDate(sourceFile) { if (!sourceFile) { @@ -49153,8 +49498,8 @@ var ts; if (element.getStart() <= position && position <= element.getEnd()) { continue; } - var name_35 = element.propertyName || element.name; - exisingImportsOrExports[name_35.text] = true; + var name_36 = element.propertyName || element.name; + exisingImportsOrExports[name_36.text] = true; } if (ts.isEmpty(exisingImportsOrExports)) { return exportsOfModule; @@ -49271,14 +49616,14 @@ var ts; var entries = []; var target = program.getCompilerOptions().target; var nameTable = getNameTable(sourceFile); - for (var name_36 in nameTable) { + for (var name_37 in nameTable) { // Skip identifiers produced only from the current location - if (nameTable[name_36] === position) { + if (nameTable[name_37] === position) { continue; } - if (!uniqueNames[name_36]) { - uniqueNames[name_36] = name_36; - var displayName = getCompletionEntryDisplayName(name_36, target, /*performCharacterChecks*/ true); + if (!uniqueNames[name_37]) { + uniqueNames[name_37] = name_37; + var displayName = getCompletionEntryDisplayName(name_37, target, /*performCharacterChecks*/ true); if (displayName) { var entry = { name: displayName, @@ -51230,19 +51575,19 @@ var ts; if (isNameOfPropertyAssignment(node)) { var objectLiteral = node.parent.parent; var contextualType = typeChecker.getContextualType(objectLiteral); - var name_37 = node.text; + var name_38 = node.text; if (contextualType) { if (contextualType.flags & 16384 /* Union */) { // This is a union type, first see if the property we are looking for is a union property (i.e. exists in all types) // if not, search the constituent types for the property - var unionProperty = contextualType.getProperty(name_37); + var unionProperty = contextualType.getProperty(name_38); if (unionProperty) { return [unionProperty]; } else { var result_6 = []; ts.forEach(contextualType.types, function (t) { - var symbol = t.getProperty(name_37); + var symbol = t.getProperty(name_38); if (symbol) { result_6.push(symbol); } @@ -51251,7 +51596,7 @@ var ts; } } else { - var symbol_1 = contextualType.getProperty(name_37); + var symbol_1 = contextualType.getProperty(name_38); if (symbol_1) { return [symbol_1]; } @@ -53628,6 +53973,14 @@ var ts; var scriptSnapshot = this.shimHost.getScriptSnapshot(fileName); return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot); }; + LanguageServiceShimHostAdapter.prototype.getScriptKind = function (fileName) { + if ("getScriptKind" in this.shimHost) { + return this.shimHost.getScriptKind(fileName); + } + else { + return 0 /* Unknown */; + } + }; LanguageServiceShimHostAdapter.prototype.getScriptVersion = function (fileName) { return this.shimHost.getScriptVersion(fileName); }; @@ -53686,8 +54039,16 @@ var ts; this.directoryExists = function (directoryName) { return _this.shimHost.directoryExists(directoryName); }; } } - CoreServicesShimHostAdapter.prototype.readDirectory = function (rootDir, extension, exclude) { - var encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)); + CoreServicesShimHostAdapter.prototype.readDirectory = function (rootDir, extension, exclude, depth) { + // Wrap the API changes for 2.0 release. This try/catch + // should be removed once TypeScript 2.0 has shipped. + var encoded; + try { + encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude), depth); + } + catch (e) { + encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)); + } return JSON.parse(encoded); }; CoreServicesShimHostAdapter.prototype.fileExists = function (fileName) { @@ -54090,6 +54451,7 @@ var ts; if (result.error) { return { options: {}, + typingOptions: {}, files: [], errors: [realizeDiagnostic(result.error, "\r\n")] }; @@ -54098,6 +54460,7 @@ var ts; var configFile = ts.parseJsonConfigFileContent(result.config, _this.host, ts.getDirectoryPath(normalizedFileName), /*existingOptions*/ {}, normalizedFileName); return { options: configFile.options, + typingOptions: configFile.typingOptions, files: configFile.fileNames, errors: realizeDiagnostics(configFile.errors, "\r\n") }; @@ -54106,6 +54469,14 @@ var ts; CoreServicesShimObject.prototype.getDefaultCompilationSettings = function () { return this.forwardJSONCall("getDefaultCompilationSettings()", function () { return ts.getDefaultCompilerOptions(); }); }; + CoreServicesShimObject.prototype.discoverTypings = function (discoverTypingsJson) { + var _this = this; + var getCanonicalFileName = ts.createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false); + return this.forwardJSONCall("discoverTypings()", function () { + var info = JSON.parse(discoverTypingsJson); + return ts.JsTyping.discoverTypings(_this.host, info.fileNames, ts.toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName), ts.toPath(info.safeListPath, info.safeListPath, getCanonicalFileName), info.packageNameToTypingLocation, info.typingOptions, info.compilerOptions); + }); + }; return CoreServicesShimObject; }(ShimBase)); var TypeScriptServicesFactory = (function () { diff --git a/lib/typescriptServices.d.ts b/lib/typescriptServices.d.ts index 3b098c5dc24..2a21625c2af 100644 --- a/lib/typescriptServices.d.ts +++ b/lib/typescriptServices.d.ts @@ -1480,6 +1480,20 @@ declare namespace ts { noImplicitUseStrict?: boolean; [option: string]: string | number | boolean; } + interface TypingOptions { + enableAutoDiscovery?: boolean; + include?: string[]; + exclude?: string[]; + [option: string]: string[] | boolean; + } + interface DiscoverTypingsInfo { + fileNames: string[]; + projectRootPath: string; + safeListPath: string; + packageNameToTypingLocation: Map; + typingOptions: TypingOptions; + compilerOptions: CompilerOptions; + } enum ModuleKind { None = 0, CommonJS = 1, @@ -1502,6 +1516,13 @@ declare namespace ts { line: number; character: number; } + enum ScriptKind { + Unknown = 0, + JS = 1, + JSX = 2, + TS = 3, + TSX = 4, + } enum ScriptTarget { ES3 = 0, ES5 = 1, @@ -1515,6 +1536,7 @@ declare namespace ts { } interface ParsedCommandLine { options: CompilerOptions; + typingOptions?: TypingOptions; fileNames: string[]; errors: Diagnostic[]; } @@ -1663,7 +1685,7 @@ declare namespace ts { declare namespace ts { function createNode(kind: SyntaxKind, pos?: number, end?: number): Node; function forEachChild(node: Node, cbNode: (node: Node) => T, cbNodeArray?: (nodes: Node[]) => T): T; - function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes?: boolean): SourceFile; + function createSourceFile(fileName: string, sourceText: string, languageVersion: ScriptTarget, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile; function updateSourceFile(sourceFile: SourceFile, newText: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; } declare namespace ts { @@ -1798,6 +1820,7 @@ declare namespace ts { getNewLine?(): string; getProjectVersion?(): string; getScriptFileNames(): string[]; + getScriptKind?(fileName: string): ScriptKind; getScriptVersion(fileName: string): string; getScriptSnapshot(fileName: string): IScriptSnapshot; getLocalizedDiagnosticMessages?(): any; @@ -2166,7 +2189,7 @@ declare namespace ts { * @parm version Current version of the file. Only used if the file was not found * in the registry and a new one was created. */ - acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile; + acquireDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile; /** * Request an updated version of an already existing SourceFile with a given fileName * and compilationSettings. The update will in-turn call updateLanguageServiceSourceFile @@ -2179,7 +2202,7 @@ declare namespace ts { * @param scriptSnapshot Text of the file. * @param version Current version of the file. */ - updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string): SourceFile; + updateDocument(fileName: string, compilationSettings: CompilerOptions, scriptSnapshot: IScriptSnapshot, version: string, scriptKind?: ScriptKind): SourceFile; /** * Informs the DocumentRegistry that a file is not needed any longer. * @@ -2303,7 +2326,7 @@ declare namespace ts { } function transpileModule(input: string, transpileOptions: TranspileOptions): TranspileOutput; function transpile(input: string, compilerOptions?: CompilerOptions, fileName?: string, diagnostics?: Diagnostic[], moduleName?: string): string; - function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean): SourceFile; + function createLanguageServiceSourceFile(fileName: string, scriptSnapshot: IScriptSnapshot, scriptTarget: ScriptTarget, version: string, setNodeParents: boolean, scriptKind?: ScriptKind): SourceFile; let disableIncrementalParsing: boolean; function updateLanguageServiceSourceFile(sourceFile: SourceFile, scriptSnapshot: IScriptSnapshot, version: string, textChangeRange: TextChangeRange, aggressiveChecks?: boolean): SourceFile; function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory?: string): DocumentRegistry; diff --git a/lib/typescriptServices.js b/lib/typescriptServices.js index 5923ee0e111..a952502f6ed 100644 --- a/lib/typescriptServices.js +++ b/lib/typescriptServices.js @@ -409,7 +409,7 @@ var ts; // its type can be specified usign a JSDoc comment. ParserContextFlags[ParserContextFlags["JavaScriptFile"] = 32] = "JavaScriptFile"; // Context flags set directly by the parser. - ParserContextFlags[ParserContextFlags["ParserGeneratedFlags"] = 31] = "ParserGeneratedFlags"; + ParserContextFlags[ParserContextFlags["ParserGeneratedFlags"] = 63] = "ParserGeneratedFlags"; // Exclude these flags when parsing a Type ParserContextFlags[ParserContextFlags["TypeExcludesFlags"] = 10] = "TypeExcludesFlags"; // Context flags computed by aggregating child flags upwards. @@ -716,6 +716,14 @@ var ts; NewLineKind[NewLineKind["LineFeed"] = 1] = "LineFeed"; })(ts.NewLineKind || (ts.NewLineKind = {})); var NewLineKind = ts.NewLineKind; + (function (ScriptKind) { + ScriptKind[ScriptKind["Unknown"] = 0] = "Unknown"; + ScriptKind[ScriptKind["JS"] = 1] = "JS"; + ScriptKind[ScriptKind["JSX"] = 2] = "JSX"; + ScriptKind[ScriptKind["TS"] = 3] = "TS"; + ScriptKind[ScriptKind["TSX"] = 4] = "TSX"; + })(ts.ScriptKind || (ts.ScriptKind = {})); + var ScriptKind = ts.ScriptKind; (function (ScriptTarget) { ScriptTarget[ScriptTarget["ES3"] = 0] = "ES3"; ScriptTarget[ScriptTarget["ES5"] = 1] = "ES5"; @@ -1137,6 +1145,14 @@ var ts; return hasOwnProperty.call(map, key); } ts.hasProperty = hasProperty; + function getKeys(map) { + var keys = []; + for (var key in map) { + keys.push(key); + } + return keys; + } + ts.getKeys = getKeys; function getProperty(map, key) { return hasOwnProperty.call(map, key) ? map[key] : undefined; } @@ -1594,6 +1610,32 @@ var ts; return pathLen > extLen && path.substr(pathLen - extLen, extLen) === extension; } ts.fileExtensionIs = fileExtensionIs; + function ensureScriptKind(fileName, scriptKind) { + // 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 we attempt + // to get the ScriptKind from the file name. If it cannot be resolved + // from the file name then the default 'TS' script kind is returned. + return (scriptKind || getScriptKindFromFileName(fileName)) || 3 /* TS */; + } + ts.ensureScriptKind = ensureScriptKind; + function getScriptKindFromFileName(fileName) { + var ext = fileName.substr(fileName.lastIndexOf(".")); + switch (ext.toLowerCase()) { + case ".js": + return 1 /* JS */; + case ".jsx": + return 2 /* JSX */; + case ".ts": + return 3 /* TS */; + case ".tsx": + return 4 /* TSX */; + default: + return 0 /* Unknown */; + } + } + ts.getScriptKindFromFileName = getScriptKindFromFileName; /** * List of supported extensions in order of file resolution precedence. */ @@ -2819,6 +2861,7 @@ var ts; Only_amd_and_system_modules_are_supported_alongside_0: { code: 6082, category: ts.DiagnosticCategory.Error, key: "Only_amd_and_system_modules_are_supported_alongside_0_6082", message: "Only 'amd' and 'system' modules are supported alongside --{0}." }, Allow_javascript_files_to_be_compiled: { code: 6083, category: ts.DiagnosticCategory.Message, key: "Allow_javascript_files_to_be_compiled_6083", message: "Allow javascript files to be compiled." }, Specifies_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit: { code: 6084, category: ts.DiagnosticCategory.Message, key: "Specifies_the_object_invoked_for_createElement_and_spread_when_targeting_react_JSX_emit_6084", message: "Specifies the object invoked for createElement and __spread when targeting 'react' JSX emit" }, + Option_0_should_have_array_of_strings_as_a_value: { code: 6103, category: ts.DiagnosticCategory.Error, key: "Option_0_should_have_array_of_strings_as_a_value_6103", message: "Option '{0}' should have array of strings as a value." }, Do_not_emit_use_strict_directives_in_module_output: { code: 6112, category: ts.DiagnosticCategory.Message, key: "Do_not_emit_use_strict_directives_in_module_output_6112", message: "Do not emit 'use strict' directives in module output." }, Variable_0_implicitly_has_an_1_type: { code: 7005, category: ts.DiagnosticCategory.Error, key: "Variable_0_implicitly_has_an_1_type_7005", message: "Variable '{0}' implicitly has an '{1}' type." }, Parameter_0_implicitly_has_an_1_type: { code: 7006, category: ts.DiagnosticCategory.Error, key: "Parameter_0_implicitly_has_an_1_type_7006", message: "Parameter '{0}' implicitly has an '{1}' type." }, @@ -2869,7 +2912,8 @@ var ts; An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17006, category: ts.DiagnosticCategory.Error, key: "An_unary_expression_with_the_0_operator_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_ex_17006", message: "An unary expression with the '{0}' operator is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Consider_enclosing_the_expression_in_parentheses: { code: 17007, category: ts.DiagnosticCategory.Error, key: "A_type_assertion_expression_is_not_allowed_in_the_left_hand_side_of_an_exponentiation_expression_Con_17007", message: "A type assertion expression is not allowed in the left-hand side of an exponentiation expression. Consider enclosing the expression in parentheses." }, JSX_element_0_has_no_corresponding_closing_tag: { code: 17008, category: ts.DiagnosticCategory.Error, key: "JSX_element_0_has_no_corresponding_closing_tag_17008", message: "JSX element '{0}' has no corresponding closing tag." }, - super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class: { code: 17009, category: ts.DiagnosticCategory.Error, key: "super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class_17009", message: "'super' must be called before accessing 'this' in the constructor of a derived class." } + super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class: { code: 17009, category: ts.DiagnosticCategory.Error, key: "super_must_be_called_before_accessing_this_in_the_constructor_of_a_derived_class_17009", message: "'super' must be called before accessing 'this' in the constructor of a derived class." }, + Unknown_typing_option_0: { code: 17010, category: ts.DiagnosticCategory.Error, key: "Unknown_typing_option_0_17010", message: "Unknown typing option '{0}'." } }; })(ts || (ts = {})); /// @@ -5548,6 +5592,9 @@ var ts; /// Given a BinaryExpression, returns SpecialPropertyAssignmentKind for the various kinds of property /// assignments we treat as special in the binder function getSpecialPropertyAssignmentKind(expression) { + if (!isInJavaScriptFile(expression)) { + return 0 /* None */; + } if (expression.kind !== 184 /* BinaryExpression */) { return 0 /* None */; } @@ -7557,10 +7604,10 @@ var ts; } } ts.forEachChild = forEachChild; - function createSourceFile(fileName, sourceText, languageVersion, setParentNodes) { + function createSourceFile(fileName, sourceText, languageVersion, setParentNodes, scriptKind) { if (setParentNodes === void 0) { setParentNodes = false; } var start = new Date().getTime(); - var result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes); + var result = Parser.parseSourceFile(fileName, sourceText, languageVersion, /*syntaxCursor*/ undefined, setParentNodes, scriptKind); ts.parseTime += new Date().getTime() - start; return result; } @@ -7685,19 +7732,19 @@ var ts; // Note: any errors at the end of the file that do not precede a regular node, should get // attached to the EOF token. var parseErrorBeforeNextFinishedNode = false; - function parseSourceFile(fileName, _sourceText, languageVersion, _syntaxCursor, setParentNodes) { - var isJavaScriptFile = ts.hasJavaScriptFileExtension(fileName) || _sourceText.lastIndexOf("// @language=javascript", 0) === 0; - initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor); - var result = parseSourceFileWorker(fileName, languageVersion, setParentNodes); + function parseSourceFile(fileName, _sourceText, languageVersion, _syntaxCursor, setParentNodes, scriptKind) { + scriptKind = ts.ensureScriptKind(fileName, scriptKind); + initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind); + var result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind); clearState(); return result; } Parser.parseSourceFile = parseSourceFile; - function getLanguageVariant(fileName) { + function getLanguageVariant(scriptKind) { // .tsx and .jsx files are treated as jsx language variant. - return ts.fileExtensionIs(fileName, ".tsx") || ts.fileExtensionIs(fileName, ".jsx") || ts.fileExtensionIs(fileName, ".js") ? 1 /* JSX */ : 0 /* Standard */; + return scriptKind === 4 /* TSX */ || scriptKind === 2 /* JSX */ || scriptKind === 1 /* JS */ ? 1 /* JSX */ : 0 /* Standard */; } - function initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor) { + function initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind) { NodeConstructor = ts.objectAllocator.getNodeConstructor(); SourceFileConstructor = ts.objectAllocator.getSourceFileConstructor(); sourceText = _sourceText; @@ -7707,13 +7754,13 @@ var ts; identifiers = {}; identifierCount = 0; nodeCount = 0; - contextFlags = isJavaScriptFile ? 32 /* JavaScriptFile */ : 0 /* None */; + contextFlags = scriptKind === 1 /* JS */ || scriptKind === 2 /* JSX */ ? 32 /* JavaScriptFile */ : 0 /* 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() { // Clear out the text the scanner is pointing at, so it doesn't keep anything alive unnecessarily. @@ -7726,8 +7773,8 @@ var ts; syntaxCursor = undefined; sourceText = undefined; } - function parseSourceFileWorker(fileName, languageVersion, setParentNodes) { - sourceFile = createSourceFile(fileName, languageVersion); + function parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind) { + sourceFile = createSourceFile(fileName, languageVersion, scriptKind); if (contextFlags & 32 /* JavaScriptFile */) { sourceFile.parserContextFlags = 32 /* JavaScriptFile */; } @@ -7784,7 +7831,7 @@ var ts; } } Parser.fixupParentReferences = fixupParentReferences; - function createSourceFile(fileName, languageVersion) { + function createSourceFile(fileName, languageVersion, scriptKind) { // 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 var sourceFile = new SourceFileConstructor(251 /* SourceFile */, /*pos*/ 0, /* end */ sourceText.length); @@ -7794,7 +7841,8 @@ var ts; sourceFile.languageVersion = languageVersion; sourceFile.fileName = ts.normalizePath(fileName); sourceFile.flags = ts.fileExtensionIs(sourceFile.fileName, ".d.ts") ? 4096 /* DeclarationFile */ : 0; - sourceFile.languageVariant = getLanguageVariant(sourceFile.fileName); + sourceFile.languageVariant = getLanguageVariant(scriptKind); + sourceFile.scriptKind = scriptKind; return sourceFile; } function setContextFlag(val, flag) { @@ -8460,7 +8508,7 @@ var ts; // differently depending on what mode it is in. // // This also applies to all our other context flags as well. - var nodeContextFlags = node.parserContextFlags & 31 /* ParserGeneratedFlags */; + var nodeContextFlags = node.parserContextFlags & 63 /* ParserGeneratedFlags */; if (nodeContextFlags !== contextFlags) { return undefined; } @@ -12100,7 +12148,7 @@ var ts; } JSDocParser.isJSDocType = isJSDocType; function parseJSDocTypeExpressionForTests(content, start, length) { - initializeState("file.js", content, 2 /* Latest */, /*isJavaScriptFile*/ true, /*_syntaxCursor:*/ undefined); + initializeState("file.js", content, 2 /* Latest */, /*_syntaxCursor:*/ undefined, 1 /* JS */); scanner.setText(content, start, length); token = scanner.scan(); var jsDocTypeExpression = parseJSDocTypeExpression(); @@ -12367,7 +12415,7 @@ var ts; } } function parseIsolatedJSDocComment(content, start, length) { - initializeState("file.js", content, 2 /* Latest */, /*isJavaScriptFile*/ true, /*_syntaxCursor:*/ undefined); + initializeState("file.js", content, 2 /* Latest */, /*_syntaxCursor:*/ undefined, 1 /* JS */); sourceFile = { languageVariant: 0 /* Standard */, text: content }; var jsDocComment = parseJSDocCommentWorker(start, length); var diagnostics = parseDiagnostics; @@ -12637,7 +12685,7 @@ var ts; if (sourceFile.statements.length === 0) { // If we don't have any statements in the current source file, then there's no real // way to incrementally parse. So just do a full parse instead. - return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true); + return Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, /*syntaxCursor*/ undefined, /*setParentNodes*/ true, sourceFile.scriptKind); } // Make sure we're not trying to incrementally update a source file more than once. Once // we do an update the original source file is considered unusbale from that point onwards. @@ -12693,7 +12741,7 @@ var ts; // inconsistent tree. Setting the parents on the new tree should be very fast. We // will immediately bail out of walking any subtrees when we can see that their parents // are already correct. - var result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true); + var result = Parser.parseSourceFile(sourceFile.fileName, newText, sourceFile.languageVersion, syntaxCursor, /*setParentNodes*/ true, sourceFile.scriptKind); return result; } IncrementalParser.updateSourceFile = updateSourceFile; @@ -13745,6 +13793,7 @@ var ts; case 146 /* GetAccessor */: case 147 /* SetAccessor */: case 153 /* FunctionType */: + case 264 /* JSDocFunctionType */: case 154 /* ConstructorType */: case 176 /* FunctionExpression */: case 177 /* ArrowFunction */: @@ -14350,7 +14399,7 @@ var ts; function bindModuleExportsAssignment(node) { // 'module.exports = expr' assignment setCommonJsModuleIndicator(node); - bindExportAssignment(node); + declareSymbol(file.symbol.exports, file.symbol, node, 4 /* Property */ | 7340032 /* Export */ | 512 /* ValueModule */, 0 /* None */); } function bindThisPropertyAssignment(node) { // Declare a 'member' in case it turns out the container was an ES5 class @@ -14380,7 +14429,8 @@ var ts; funcSymbol.members = {}; } // Declare the method/property - declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, 4 /* Property */, 107455 /* PropertyExcludes */); + // It's acceptable for multiple prototype property assignments of the same identifier to occur + declareSymbol(funcSymbol.members, funcSymbol, leftSideOfAssignment, 4 /* Property */, 107455 /* PropertyExcludes */ & ~4 /* Property */); } function bindCallExpression(node) { // We're only inspecting call expressions to detect CommonJS modules, so we can skip @@ -15439,7 +15489,9 @@ var ts; function getTargetOfImportClause(node) { var moduleSymbol = resolveExternalModuleName(node, node.parent.moduleSpecifier); if (moduleSymbol) { - var exportDefaultSymbol = resolveSymbol(moduleSymbol.exports["default"]); + var exportDefaultSymbol = moduleSymbol.exports["export="] ? + getPropertyOfType(getTypeOfSymbol(moduleSymbol.exports["export="]), "default") : + resolveSymbol(moduleSymbol.exports["default"]); if (!exportDefaultSymbol && !allowSyntheticDefaultImports) { error(node.name, ts.Diagnostics.Module_0_has_no_default_export, symbolToString(moduleSymbol)); } @@ -15508,8 +15560,15 @@ var ts; if (targetSymbol) { var name_9 = specifier.propertyName || specifier.name; if (name_9.text) { + var symbolFromVariable = void 0; + // First check if module was specified with "export=". If so, get the member from the resolved type + if (moduleSymbol && moduleSymbol.exports && moduleSymbol.exports["export="]) { + symbolFromVariable = getPropertyOfType(getTypeOfSymbol(targetSymbol), name_9.text); + } + else { + symbolFromVariable = getPropertyOfVariable(targetSymbol, name_9.text); + } var symbolFromModule = getExportOfModule(targetSymbol, name_9.text); - var symbolFromVariable = getPropertyOfVariable(targetSymbol, name_9.text); var symbol = symbolFromModule && symbolFromVariable ? combineValueAndTypeSymbols(symbolFromVariable, symbolFromModule) : symbolFromModule || symbolFromVariable; @@ -19403,7 +19462,7 @@ var ts; for (var i = 0; i < checkCount; i++) { var s = i < sourceMax ? getTypeOfSymbol(sourceParams[i]) : getRestTypeOfSignature(source); var t = i < targetMax ? getTypeOfSymbol(targetParams[i]) : getRestTypeOfSignature(target); - var related = compareTypes(t, s, /*reportErrors*/ false) || compareTypes(s, t, reportErrors); + var related = compareTypes(s, t, /*reportErrors*/ false) || compareTypes(t, s, reportErrors); if (!related) { if (reportErrors) { errorReporter(ts.Diagnostics.Types_of_parameters_0_and_1_are_incompatible, sourceParams[i < sourceMax ? i : sourceMax].name, targetParams[i < targetMax ? i : targetMax].name); @@ -20528,8 +20587,10 @@ var ts; function inferTypes(context, source, target) { var sourceStack; var targetStack; + var maxDepth = 5; var depth = 0; var inferiority = 0; + var visited = {}; inferFromTypes(source, target); function isInProcess(source, target) { for (var i = 0; i < depth; i++) { @@ -20655,9 +20716,19 @@ var ts; if (isInProcess(source, target)) { return; } + // we delibirately limit the depth we examine to infer types: this speeds up the overall inference process + // and user rarely expects inferences to be made from the deeply nested constituents. + if (depth > maxDepth) { + return; + } if (isDeeplyNestedGeneric(source, sourceStack, depth) && isDeeplyNestedGeneric(target, targetStack, depth)) { return; } + var key = source.id + "," + target.id; + if (ts.hasProperty(visited, key)) { + return; + } + visited[key] = true; if (depth === 0) { sourceStack = []; targetStack = []; @@ -39261,7 +39332,7 @@ var ts; /* @internal */ ts.ioWriteTime = 0; /** The version of the TypeScript compiler release */ var emptyArray = []; - ts.version = "1.8.5"; + ts.version = "1.8.9"; function findConfigFile(searchPath, fileExists) { var fileName = "tsconfig.json"; while (true) { @@ -40972,6 +41043,7 @@ var ts; return { options: options, fileNames: getFileNames(), + typingOptions: getTypingOptions(), errors: errors }; function getFileNames() { @@ -41031,6 +41103,34 @@ var ts; } return fileNames; } + function getTypingOptions() { + var options = ts.getBaseFileName(configFileName) === "jsconfig.json" + ? { enableAutoDiscovery: true, include: [], exclude: [] } + : { enableAutoDiscovery: false, include: [], exclude: [] }; + var jsonTypingOptions = json["typingOptions"]; + if (jsonTypingOptions) { + for (var id in jsonTypingOptions) { + if (id === "enableAutoDiscovery") { + if (typeof jsonTypingOptions[id] === "boolean") { + options.enableAutoDiscovery = jsonTypingOptions[id]; + } + else { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Unknown_typing_option_0, id)); + } + } + else if (id === "include") { + options.include = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + } + else if (id === "exclude") { + options.exclude = convertJsonOptionToStringArray(id, jsonTypingOptions[id], errors); + } + else { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Unknown_typing_option_0, id)); + } + } + } + return options; + } } ts.parseJsonConfigFileContent = parseJsonConfigFileContent; function convertCompilerOptionsFromJson(jsonOptions, basePath, configFileName) { @@ -41079,6 +41179,30 @@ var ts; return { options: options, errors: errors }; } ts.convertCompilerOptionsFromJson = convertCompilerOptionsFromJson; + function convertJsonOptionToStringArray(optionName, optionJson, errors, func) { + var items = []; + var invalidOptionType = false; + if (!ts.isArray(optionJson)) { + invalidOptionType = true; + } + else { + for (var _i = 0, _a = optionJson; _i < _a.length; _i++) { + var element = _a[_i]; + if (typeof element === "string") { + var item = func ? func(element) : element; + items.push(item); + } + else { + invalidOptionType = true; + break; + } + } + } + if (invalidOptionType) { + errors.push(ts.createCompilerDiagnostic(ts.Diagnostics.Option_0_should_have_array_of_strings_as_a_value, optionName)); + } + return items; + } })(ts || (ts = {})); /* @internal */ var ts; @@ -43742,6 +43866,212 @@ var ts; return name; } ts.stripQuotes = stripQuotes; + function scriptKindIs(fileName, host) { + var scriptKinds = []; + for (var _i = 2; _i < arguments.length; _i++) { + scriptKinds[_i - 2] = arguments[_i]; + } + var scriptKind = getScriptKind(fileName, host); + return ts.forEach(scriptKinds, function (k) { return k === scriptKind; }); + } + ts.scriptKindIs = scriptKindIs; + function getScriptKind(fileName, host) { + // First check to see if the script kind can be determined from the file name + var scriptKind = ts.getScriptKindFromFileName(fileName); + if (scriptKind === 0 /* Unknown */ && host && host.getScriptKind) { + // Next check to see if the host can resolve the script kind + scriptKind = host.getScriptKind(fileName); + } + return ts.ensureScriptKind(fileName, scriptKind); + } + ts.getScriptKind = getScriptKind; +})(ts || (ts = {})); +// Copyright (c) Microsoft. All rights reserved. Licensed under the Apache License, Version 2.0. +// See LICENSE.txt in the project root for complete license information. +/// +/* @internal */ +var ts; +(function (ts) { + var JsTyping; + (function (JsTyping) { + ; + ; + // A map of loose file names to library names + // that we are confident require typings + var safeList; + /** + * @param host is the object providing I/O related operations. + * @param fileNames are the file names that belong to the same project + * @param projectRootPath is the path to the project root directory + * @param safeListPath is the path used to retrieve the safe list + * @param packageNameToTypingLocation is the map of package names to their cached typing locations + * @param typingOptions are used to customize the typing inference process + * @param compilerOptions are used as a source for typing inference + */ + function discoverTypings(host, fileNames, projectRootPath, safeListPath, packageNameToTypingLocation, typingOptions, compilerOptions) { + // A typing name to typing file path mapping + var inferredTypings = {}; + if (!typingOptions || !typingOptions.enableAutoDiscovery) { + return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] }; + } + // Only infer typings for .js and .jsx files + fileNames = ts.filter(ts.map(fileNames, ts.normalizePath), function (f) { return ts.scriptKindIs(f, /*LanguageServiceHost*/ undefined, 1 /* JS */, 2 /* JSX */); }); + if (!safeList) { + var result = ts.readConfigFile(safeListPath, function (path) { return host.readFile(path); }); + if (result.config) { + safeList = result.config; + } + else { + safeList = {}; + } + } + var filesToWatch = []; + // Directories to search for package.json, bower.json and other typing information + var searchDirs = []; + var exclude = []; + mergeTypings(typingOptions.include); + exclude = typingOptions.exclude || []; + var possibleSearchDirs = ts.map(fileNames, ts.getDirectoryPath); + if (projectRootPath !== undefined) { + possibleSearchDirs.push(projectRootPath); + } + searchDirs = ts.deduplicate(possibleSearchDirs); + for (var _i = 0, searchDirs_1 = searchDirs; _i < searchDirs_1.length; _i++) { + var searchDir = searchDirs_1[_i]; + var packageJsonPath = ts.combinePaths(searchDir, "package.json"); + getTypingNamesFromJson(packageJsonPath, filesToWatch); + var bowerJsonPath = ts.combinePaths(searchDir, "bower.json"); + getTypingNamesFromJson(bowerJsonPath, filesToWatch); + var nodeModulesPath = ts.combinePaths(searchDir, "node_modules"); + getTypingNamesFromNodeModuleFolder(nodeModulesPath); + } + getTypingNamesFromSourceFileNames(fileNames); + // Add the cached typing locations for inferred typings that are already installed + for (var name_34 in packageNameToTypingLocation) { + if (ts.hasProperty(inferredTypings, name_34) && !inferredTypings[name_34]) { + inferredTypings[name_34] = packageNameToTypingLocation[name_34]; + } + } + // Remove typings that the user has added to the exclude list + for (var _a = 0, exclude_1 = exclude; _a < exclude_1.length; _a++) { + var excludeTypingName = exclude_1[_a]; + delete inferredTypings[excludeTypingName]; + } + var newTypingNames = []; + var cachedTypingPaths = []; + for (var typing in inferredTypings) { + if (inferredTypings[typing] !== undefined) { + cachedTypingPaths.push(inferredTypings[typing]); + } + else { + newTypingNames.push(typing); + } + } + return { cachedTypingPaths: cachedTypingPaths, newTypingNames: newTypingNames, filesToWatch: filesToWatch }; + /** + * Merge a given list of typingNames to the inferredTypings map + */ + function mergeTypings(typingNames) { + if (!typingNames) { + return; + } + for (var _i = 0, typingNames_1 = typingNames; _i < typingNames_1.length; _i++) { + var typing = typingNames_1[_i]; + if (!ts.hasProperty(inferredTypings, typing)) { + inferredTypings[typing] = undefined; + } + } + } + /** + * Get the typing info from common package manager json files like package.json or bower.json + */ + function getTypingNamesFromJson(jsonPath, filesToWatch) { + var result = ts.readConfigFile(jsonPath, function (path) { return host.readFile(path); }); + if (result.config) { + var jsonConfig = result.config; + filesToWatch.push(jsonPath); + if (jsonConfig.dependencies) { + mergeTypings(ts.getKeys(jsonConfig.dependencies)); + } + if (jsonConfig.devDependencies) { + mergeTypings(ts.getKeys(jsonConfig.devDependencies)); + } + if (jsonConfig.optionalDependencies) { + mergeTypings(ts.getKeys(jsonConfig.optionalDependencies)); + } + if (jsonConfig.peerDependencies) { + mergeTypings(ts.getKeys(jsonConfig.peerDependencies)); + } + } + } + /** + * Infer typing names from given file names. For example, the file name "jquery-min.2.3.4.js" + * should be inferred to the 'jquery' typing name; and "angular-route.1.2.3.js" should be inferred + * to the 'angular-route' typing name. + * @param fileNames are the names for source files in the project + */ + function getTypingNamesFromSourceFileNames(fileNames) { + var jsFileNames = ts.filter(fileNames, ts.hasJavaScriptFileExtension); + var inferredTypingNames = ts.map(jsFileNames, function (f) { return ts.removeFileExtension(ts.getBaseFileName(f.toLowerCase())); }); + var cleanedTypingNames = ts.map(inferredTypingNames, function (f) { return f.replace(/((?:\.|-)min(?=\.|$))|((?:-|\.)\d+)/g, ""); }); + if (safeList === undefined) { + mergeTypings(cleanedTypingNames); + } + else { + mergeTypings(ts.filter(cleanedTypingNames, function (f) { return ts.hasProperty(safeList, f); })); + } + var hasJsxFile = ts.forEach(fileNames, function (f) { return ts.scriptKindIs(f, /*LanguageServiceHost*/ undefined, 2 /* JSX */); }); + if (hasJsxFile) { + mergeTypings(["react"]); + } + } + /** + * Infer typing names from node_module folder + * @param nodeModulesPath is the path to the "node_modules" folder + */ + function getTypingNamesFromNodeModuleFolder(nodeModulesPath) { + // Todo: add support for ModuleResolutionHost too + if (!host.directoryExists(nodeModulesPath)) { + return; + } + var typingNames = []; + var fileNames = host.readDirectory(nodeModulesPath, "*.json", /*exclude*/ undefined, /*depth*/ 2); + for (var _i = 0, fileNames_1 = fileNames; _i < fileNames_1.length; _i++) { + var fileName = fileNames_1[_i]; + var normalizedFileName = ts.normalizePath(fileName); + if (ts.getBaseFileName(normalizedFileName) !== "package.json") { + continue; + } + var result = ts.readConfigFile(normalizedFileName, function (path) { return host.readFile(path); }); + if (!result.config) { + continue; + } + var packageJson = result.config; + // npm 3's package.json contains a "_requiredBy" field + // we should include all the top level module names for npm 2, and only module names whose + // "_requiredBy" field starts with "#" or equals "/" for npm 3. + if (packageJson._requiredBy && + ts.filter(packageJson._requiredBy, function (r) { return r[0] === "#" || r === "/"; }).length === 0) { + continue; + } + // If the package has its own d.ts typings, those will take precedence. Otherwise the package name will be used + // to download d.ts files from DefinitelyTyped + if (!packageJson.name) { + continue; + } + if (packageJson.typings) { + var absolutePath = ts.getNormalizedAbsolutePath(packageJson.typings, ts.getDirectoryPath(normalizedFileName)); + inferredTypings[packageJson.name] = absolutePath; + } + else { + typingNames.push(packageJson.name); + } + } + mergeTypings(typingNames); + } + } + JsTyping.discoverTypings = discoverTypings; + })(JsTyping = ts.JsTyping || (ts.JsTyping = {})); })(ts || (ts = {})); /// /// @@ -44452,9 +44782,9 @@ var ts; } Rules.prototype.getRuleName = function (rule) { var o = this; - for (var name_34 in o) { - if (o[name_34] === rule) { - return name_34; + for (var name_35 in o) { + if (o[name_35] === rule) { + return name_35; } } throw new Error("Unknown rule"); @@ -45129,12 +45459,20 @@ var ts; if (line === 0) { return []; } - // get the span for the previous\current line + // After the enter key, the cursor is now at a new line. The new line may or may not contain non-whitespace characters. + // If the new line has only whitespaces, we won't want to format this line, because that would remove the indentation as + // trailing whitespaces. So the end of the formatting span should be the later one between: + // 1. the end of the previous line + // 2. the last non-whitespace character in the current line + var endOfFormatSpan = ts.getEndLinePosition(line, sourceFile); + while (ts.isWhiteSpace(sourceFile.text.charCodeAt(endOfFormatSpan)) && !ts.isLineBreak(sourceFile.text.charCodeAt(endOfFormatSpan))) { + endOfFormatSpan--; + } var span = { // get start position for the previous line pos: ts.getStartPositionOfLine(line - 1, sourceFile), - // get end position for the current line (end value is exclusive so add 1 to the result) - end: ts.getEndLinePosition(line, sourceFile) + 1 + // end value is exclusive so add 1 to the result + end: endOfFormatSpan + 1 }; return formatSpan(span, sourceFile, options, rulesProvider, 2 /* FormatOnEnter */); } @@ -46456,12 +46794,13 @@ var ts; /// /// /// +/// /// /// var ts; (function (ts) { /** The version of the language service API */ - ts.servicesVersion = "0.4"; + ts.servicesVersion = "0.5"; var ScriptSnapshot; (function (ScriptSnapshot) { var StringScriptSnapshot = (function () { @@ -47449,7 +47788,8 @@ var ts; entry = { hostFileName: fileName, version: this.host.getScriptVersion(fileName), - scriptSnapshot: scriptSnapshot + scriptSnapshot: scriptSnapshot, + scriptKind: ts.getScriptKind(fileName, this.host) }; } this.fileNameToEntry.set(path, entry); @@ -47497,11 +47837,12 @@ var ts; // The host does not know about this file. throw new Error("Could not find file: '" + fileName + "'."); } + var scriptKind = ts.getScriptKind(fileName, this.host); var version = this.host.getScriptVersion(fileName); var sourceFile; if (this.currentFileName !== fileName) { // This is a new file, just parse it - sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, 2 /* Latest */, version, /*setNodeParents*/ true); + sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, 2 /* Latest */, version, /*setNodeParents*/ true, scriptKind); } else if (this.currentFileVersion !== version) { // This is the same file, just a newer version. Incrementally parse the file. @@ -47601,9 +47942,9 @@ var ts; return output.outputText; } ts.transpile = transpile; - function createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, setNodeParents) { + function createLanguageServiceSourceFile(fileName, scriptSnapshot, scriptTarget, version, setNodeParents, scriptKind) { var text = scriptSnapshot.getText(0, scriptSnapshot.getLength()); - var sourceFile = ts.createSourceFile(fileName, text, scriptTarget, setNodeParents); + var sourceFile = ts.createSourceFile(fileName, text, scriptTarget, setNodeParents, scriptKind); setSourceFileFields(sourceFile, scriptSnapshot, version); return sourceFile; } @@ -47656,7 +47997,7 @@ var ts; } } // Otherwise, just create a new source file. - return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true); + return createLanguageServiceSourceFile(sourceFile.fileName, scriptSnapshot, sourceFile.languageVersion, version, /*setNodeParents*/ true, sourceFile.scriptKind); } ts.updateLanguageServiceSourceFile = updateLanguageServiceSourceFile; function createDocumentRegistry(useCaseSensitiveFileNames, currentDirectory) { @@ -47695,20 +48036,20 @@ var ts; }); return JSON.stringify(bucketInfoArray, undefined, 2); } - function acquireDocument(fileName, compilationSettings, scriptSnapshot, version) { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true); + function acquireDocument(fileName, compilationSettings, scriptSnapshot, version, scriptKind) { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ true, scriptKind); } - function updateDocument(fileName, compilationSettings, scriptSnapshot, version) { - return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false); + function updateDocument(fileName, compilationSettings, scriptSnapshot, version, scriptKind) { + return acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, /*acquiring*/ false, scriptKind); } - function acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, acquiring) { + function acquireOrUpdateDocument(fileName, compilationSettings, scriptSnapshot, version, acquiring, scriptKind) { var bucket = getBucketForCompilationSettings(compilationSettings, /*createIfMissing*/ true); var path = ts.toPath(fileName, currentDirectory, getCanonicalFileName); var entry = bucket.get(path); if (!entry) { ts.Debug.assert(acquiring, "How could we be trying to update a document that the registry doesn't have?"); // Have never seen this file with these settings. Create a new source file for it. - var sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false); + var sourceFile = createLanguageServiceSourceFile(fileName, scriptSnapshot, compilationSettings.target, version, /*setNodeParents*/ false, scriptKind); entry = { sourceFile: sourceFile, languageServiceRefCount: 0, @@ -48396,11 +48737,15 @@ var 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. - return documentRegistry.updateDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + // 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" + ts.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); } } // Could not find this file in the old program, create a new SourceFile for it. - return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version); + return documentRegistry.acquireDocument(fileName, newSettings, hostFileInformation.scriptSnapshot, hostFileInformation.version, hostFileInformation.scriptKind); } function sourceFileUpToDate(sourceFile) { if (!sourceFile) { @@ -49153,8 +49498,8 @@ var ts; if (element.getStart() <= position && position <= element.getEnd()) { continue; } - var name_35 = element.propertyName || element.name; - exisingImportsOrExports[name_35.text] = true; + var name_36 = element.propertyName || element.name; + exisingImportsOrExports[name_36.text] = true; } if (ts.isEmpty(exisingImportsOrExports)) { return exportsOfModule; @@ -49271,14 +49616,14 @@ var ts; var entries = []; var target = program.getCompilerOptions().target; var nameTable = getNameTable(sourceFile); - for (var name_36 in nameTable) { + for (var name_37 in nameTable) { // Skip identifiers produced only from the current location - if (nameTable[name_36] === position) { + if (nameTable[name_37] === position) { continue; } - if (!uniqueNames[name_36]) { - uniqueNames[name_36] = name_36; - var displayName = getCompletionEntryDisplayName(name_36, target, /*performCharacterChecks*/ true); + if (!uniqueNames[name_37]) { + uniqueNames[name_37] = name_37; + var displayName = getCompletionEntryDisplayName(name_37, target, /*performCharacterChecks*/ true); if (displayName) { var entry = { name: displayName, @@ -51230,19 +51575,19 @@ var ts; if (isNameOfPropertyAssignment(node)) { var objectLiteral = node.parent.parent; var contextualType = typeChecker.getContextualType(objectLiteral); - var name_37 = node.text; + var name_38 = node.text; if (contextualType) { if (contextualType.flags & 16384 /* Union */) { // This is a union type, first see if the property we are looking for is a union property (i.e. exists in all types) // if not, search the constituent types for the property - var unionProperty = contextualType.getProperty(name_37); + var unionProperty = contextualType.getProperty(name_38); if (unionProperty) { return [unionProperty]; } else { var result_6 = []; ts.forEach(contextualType.types, function (t) { - var symbol = t.getProperty(name_37); + var symbol = t.getProperty(name_38); if (symbol) { result_6.push(symbol); } @@ -51251,7 +51596,7 @@ var ts; } } else { - var symbol_1 = contextualType.getProperty(name_37); + var symbol_1 = contextualType.getProperty(name_38); if (symbol_1) { return [symbol_1]; } @@ -53628,6 +53973,14 @@ var ts; var scriptSnapshot = this.shimHost.getScriptSnapshot(fileName); return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot); }; + LanguageServiceShimHostAdapter.prototype.getScriptKind = function (fileName) { + if ("getScriptKind" in this.shimHost) { + return this.shimHost.getScriptKind(fileName); + } + else { + return 0 /* Unknown */; + } + }; LanguageServiceShimHostAdapter.prototype.getScriptVersion = function (fileName) { return this.shimHost.getScriptVersion(fileName); }; @@ -53686,8 +54039,16 @@ var ts; this.directoryExists = function (directoryName) { return _this.shimHost.directoryExists(directoryName); }; } } - CoreServicesShimHostAdapter.prototype.readDirectory = function (rootDir, extension, exclude) { - var encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)); + CoreServicesShimHostAdapter.prototype.readDirectory = function (rootDir, extension, exclude, depth) { + // Wrap the API changes for 2.0 release. This try/catch + // should be removed once TypeScript 2.0 has shipped. + var encoded; + try { + encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude), depth); + } + catch (e) { + encoded = this.shimHost.readDirectory(rootDir, extension, JSON.stringify(exclude)); + } return JSON.parse(encoded); }; CoreServicesShimHostAdapter.prototype.fileExists = function (fileName) { @@ -54090,6 +54451,7 @@ var ts; if (result.error) { return { options: {}, + typingOptions: {}, files: [], errors: [realizeDiagnostic(result.error, "\r\n")] }; @@ -54098,6 +54460,7 @@ var ts; var configFile = ts.parseJsonConfigFileContent(result.config, _this.host, ts.getDirectoryPath(normalizedFileName), /*existingOptions*/ {}, normalizedFileName); return { options: configFile.options, + typingOptions: configFile.typingOptions, files: configFile.fileNames, errors: realizeDiagnostics(configFile.errors, "\r\n") }; @@ -54106,6 +54469,14 @@ var ts; CoreServicesShimObject.prototype.getDefaultCompilationSettings = function () { return this.forwardJSONCall("getDefaultCompilationSettings()", function () { return ts.getDefaultCompilerOptions(); }); }; + CoreServicesShimObject.prototype.discoverTypings = function (discoverTypingsJson) { + var _this = this; + var getCanonicalFileName = ts.createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false); + return this.forwardJSONCall("discoverTypings()", function () { + var info = JSON.parse(discoverTypingsJson); + return ts.JsTyping.discoverTypings(_this.host, info.fileNames, ts.toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName), ts.toPath(info.safeListPath, info.safeListPath, getCanonicalFileName), info.packageNameToTypingLocation, info.typingOptions, info.compilerOptions); + }); + }; return CoreServicesShimObject; }(ShimBase)); var TypeScriptServicesFactory = (function () {