Merge pull request #7440 from Microsoft/port-acquiredts-and-scriptkind-from-master-to-release-1.8

Port acquire d.ts and scriptKind from master to release-1.8
This commit is contained in:
jramsay
2016-03-09 16:38:32 -08:00
13 changed files with 467 additions and 42 deletions
+1
View File
@@ -920,6 +920,7 @@ var servicesLintTargets = [
"patternMatcher.ts",
"services.ts",
"shims.ts",
"jsTyping.ts"
].map(function (s) {
return path.join(servicesDirectory, s);
});
+57 -3
View File
@@ -511,6 +511,7 @@ namespace ts {
return {
options,
fileNames: getFileNames(),
typingOptions: getTypingOptions(),
errors
};
@@ -575,6 +576,35 @@ 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 {
errors.push(createCompilerDiagnostic(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(createCompilerDiagnostic(Diagnostics.Unknown_typing_option_0, id));
}
}
}
return options;
}
}
export function convertCompilerOptionsFromJson(jsonOptions: any, basePath: string, configFileName?: string): { options: CompilerOptions, errors: Diagnostic[] } {
@@ -610,9 +640,9 @@ namespace ts {
}
if (opt.isFilePath) {
value = normalizePath(combinePaths(basePath, value));
if (value === "") {
value = ".";
}
if (value === "") {
value = ".";
}
}
options[opt.name] = value;
}
@@ -627,4 +657,28 @@ namespace ts {
return { options, errors };
}
function convertJsonOptionToStringArray(optionName: string, optionJson: any, errors: Diagnostic[], func?: (element: string) => string): string[] {
const items: string[] = [];
let invalidOptionType = false;
if (!isArray(optionJson)) {
invalidOptionType = true;
}
else {
for (const element of <any[]>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;
}
}
+34
View File
@@ -280,6 +280,14 @@ namespace ts {
return hasOwnProperty.call(map, key);
}
export function getKeys<T>(map: Map<T>): string[] {
const keys: string[] = [];
for (const key in map) {
keys.push(key);
}
return keys;
}
export function getProperty<T>(map: Map<T>, key: string): T {
return hasOwnProperty.call(map, key) ? map[key] : undefined;
}
@@ -769,6 +777,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.
*/
+8 -1
View File
@@ -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
@@ -2654,5 +2657,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
}
}
+21 -19
View File
@@ -399,9 +399,9 @@ 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;
@@ -533,23 +533,24 @@ 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;
initializeState(fileName, _sourceText, languageVersion, isJavaScriptFile, _syntaxCursor);
export function parseSourceFile(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, setParentNodes?: boolean, scriptKind?: ScriptKind): SourceFile {
scriptKind = ensureScriptKind(fileName, scriptKind);
const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes);
initializeState(fileName, _sourceText, languageVersion, _syntaxCursor, scriptKind);
const result = parseSourceFileWorker(fileName, languageVersion, setParentNodes, scriptKind);
clearState();
return result;
}
function getLanguageVariant(fileName: string) {
function getLanguageVariant(scriptKind: ScriptKind) {
// .tsx and .jsx files are treated as jsx language variant.
return fileExtensionIs(fileName, ".tsx") || fileExtensionIs(fileName, ".jsx") || fileExtensionIs(fileName, ".js") ? LanguageVariant.JSX : LanguageVariant.Standard;
return scriptKind === ScriptKind.TSX || scriptKind === ScriptKind.JSX || scriptKind === ScriptKind.JS ? LanguageVariant.JSX : LanguageVariant.Standard;
}
function initializeState(fileName: string, _sourceText: string, languageVersion: ScriptTarget, isJavaScriptFile: boolean, _syntaxCursor: IncrementalParser.SyntaxCursor) {
function initializeState(fileName: string, _sourceText: string, languageVersion: ScriptTarget, _syntaxCursor: IncrementalParser.SyntaxCursor, scriptKind: ScriptKind) {
NodeConstructor = objectAllocator.getNodeConstructor();
SourceFileConstructor = objectAllocator.getSourceFileConstructor();
@@ -562,14 +563,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() {
@@ -585,8 +586,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;
@@ -656,7 +657,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 = <SourceFile>new SourceFileConstructor(SyntaxKind.SourceFile, /*pos*/ 0, /* end */ sourceText.length);
@@ -667,7 +668,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);
sourceFile.languageVariant = getLanguageVariant(scriptKind);
sourceFile.scriptKind = scriptKind;
return sourceFile;
}
@@ -5604,7 +5606,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();
@@ -5923,7 +5925,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 = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content };
const jsDocComment = parseJSDocCommentWorker(start, length);
const diagnostics = parseDiagnostics;
@@ -6249,7 +6251,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, sourceFile.scriptKind);
}
// Make sure we're not trying to incrementally update a source file more than once. Once
@@ -6313,7 +6315,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, sourceFile.scriptKind);
return result;
}
+27
View File
@@ -1,3 +1,4 @@
namespace ts {
export interface Map<T> {
[index: string]: T;
@@ -1559,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;
@@ -2447,6 +2449,22 @@ namespace ts {
[option: string]: string | number | boolean;
}
export interface TypingOptions {
enableAutoDiscovery?: boolean;
include?: string[];
exclude?: string[];
[option: string]: string[] | boolean;
}
export interface DiscoverTypingsInfo {
fileNames: string[]; // The file names that belong to the same project.
projectRootPath: string; // The path to the project root directory
safeListPath: string; // The path used to retrieve the safe list
packageNameToTypingLocation: Map<string>; // 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 {
None = 0,
CommonJS = 1,
@@ -2476,6 +2494,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,
@@ -2497,6 +2523,7 @@ namespace ts {
export interface ParsedCommandLine {
options: CompilerOptions;
typingOptions?: TypingOptions;
fileNames: string[];
errors: Diagnostic[];
}
+2
View File
@@ -183,6 +183,7 @@ namespace Harness.LanguageService {
const script = this.getScriptInfo(fileName);
return script ? new ScriptSnapshot(script) : undefined;
}
getScriptKind(fileName: string): ts.ScriptKind { return ts.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): ts.ScriptKind { return this.nativeHost.getScriptKind(fileName); }
getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); }
getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); }
+4
View File
@@ -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();
}
+227
View File
@@ -0,0 +1,227 @@
// 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.
/// <reference path='services.ts' />
/* @internal */
namespace ts.JsTyping {
export 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[];
};
interface PackageJson {
_requiredBy?: string[];
dependencies?: Map<string>;
devDependencies?: Map<string>;
name?: string;
optionalDependencies?: Map<string>;
peerDependencies?: Map<string>;
typings?: string;
};
// A map of loose file names to library names
// that we are confident require typings
let safeList: Map<string>;
/**
* @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
*/
export function discoverTypings(
host: TypingResolutionHost,
fileNames: string[],
projectRootPath: Path,
safeListPath: Path,
packageNameToTypingLocation: Map<string>,
typingOptions: TypingOptions,
compilerOptions: CompilerOptions):
{ cachedTypingPaths: string[], newTypingNames: string[], filesToWatch: string[] } {
// A typing name to typing file path mapping
const inferredTypings: Map<string> = {};
if (!typingOptions || !typingOptions.enableAutoDiscovery) {
return { cachedTypingPaths: [], newTypingNames: [], filesToWatch: [] };
}
// Only infer typings for .js and .jsx files
fileNames = filter(map(fileNames, normalizePath), f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.JS, ScriptKind.JSX));
if (!safeList) {
const result = readConfigFile(safeListPath, (path: string) => host.readFile(path));
if (result.config) {
safeList = result.config;
}
else {
safeList = {};
}
}
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 || [];
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);
}
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];
}
}
// 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 (!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: string, filesToWatch: string[]) {
const result = readConfigFile(jsonPath, (path: string) => host.readFile(path));
if (result.config) {
const jsonConfig: PackageJson = result.config;
filesToWatch.push(jsonPath);
if (jsonConfig.dependencies) {
mergeTypings(getKeys(jsonConfig.dependencies));
}
if (jsonConfig.devDependencies) {
mergeTypings(getKeys(jsonConfig.devDependencies));
}
if (jsonConfig.optionalDependencies) {
mergeTypings(getKeys(jsonConfig.optionalDependencies));
}
if (jsonConfig.peerDependencies) {
mergeTypings(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: string[]) {
const jsFileNames = filter(fileNames, hasJavaScriptFileExtension);
const inferredTypingNames = map(jsFileNames, f => removeFileExtension(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 hasJsxFile = forEach(fileNames, f => scriptKindIs(f, /*LanguageServiceHost*/ undefined, ScriptKind.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: string) {
// Todo: add support for ModuleResolutionHost too
if (!host.directoryExists(nodeModulesPath)) {
return;
}
const typingNames: string[] = [];
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, (path: string) => host.readFile(path));
if (!result.config) {
continue;
}
const packageJson: 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 &&
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
if (!packageJson.name) {
continue;
}
if (packageJson.typings) {
const absolutePath = getNormalizedAbsolutePath(packageJson.typings, getDirectoryPath(normalizedFileName));
inferredTypings[packageJson.name] = absolutePath;
}
else {
typingNames.push(packageJson.name);
}
}
mergeTypings(typingNames);
}
}
}
+31 -16
View File
@@ -7,12 +7,13 @@
/// <reference path='patternMatcher.ts' />
/// <reference path='signatureHelp.ts' />
/// <reference path='utilities.ts' />
/// <reference path='jsTyping.ts' />
/// <reference path='formatting\formatting.ts' />
/// <reference path='formatting\smartIndenter.ts' />
namespace ts {
/** The version of the language service API */
export const servicesVersion = "0.4";
export const servicesVersion = "0.5";
export interface Node {
getSourceFile(): SourceFile;
@@ -805,6 +806,7 @@ namespace ts {
public identifierCount: number;
public symbolCount: number;
public version: string;
public scriptKind: ScriptKind;
public languageVersion: ScriptTarget;
public languageVariant: LanguageVariant;
public identifiers: Map<string>;
@@ -1018,6 +1020,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 +1470,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 +1489,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 +1661,7 @@ namespace ts {
hostFileName: string;
version: string;
scriptSnapshot: IScriptSnapshot;
scriptKind: ScriptKind;
}
interface DocumentRegistryEntry {
@@ -1750,7 +1756,8 @@ namespace ts {
entry = {
hostFileName: fileName,
version: this.host.getScriptVersion(fileName),
scriptSnapshot: scriptSnapshot
scriptSnapshot: scriptSnapshot,
scriptKind: getScriptKind(fileName, this.host)
};
}
@@ -1816,12 +1823,13 @@ namespace ts {
throw new Error("Could not find file: '" + fileName + "'.");
}
const scriptKind = getScriptKind(fileName, this.host);
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.
@@ -1952,9 +1960,9 @@ 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;
}
@@ -2016,7 +2024,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, sourceFile.scriptKind);
}
export function createDocumentRegistry(useCaseSensitiveFileNames?: boolean, currentDirectory = ""): DocumentRegistry {
@@ -2058,12 +2066,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 +2079,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 +2089,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,
@@ -2844,14 +2853,20 @@ 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);
// 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);
}
// 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 {
+39 -3
View File
@@ -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;
@@ -77,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;
}
///
@@ -229,6 +230,7 @@ namespace ts {
getPreProcessedFileInfo(fileName: string, sourceText: IScriptSnapshot): string;
getTSConfigFileInfo(fileName: string, sourceText: IScriptSnapshot): string;
getDefaultCompilationSettings(): string;
discoverTypings(discoverTypingsJson: string): string;
}
function logInternalError(logger: Logger, err: Error) {
@@ -344,6 +346,15 @@ namespace ts {
return scriptSnapshot && new ScriptSnapshotShimAdapter(scriptSnapshot);
}
public getScriptKind(fileName: string): ScriptKind {
if ("getScriptKind" in this.shimHost) {
return this.shimHost.getScriptKind(fileName);
}
else {
return ScriptKind.Unknown;
}
}
public getScriptVersion(fileName: string): string {
return this.shimHost.getScriptVersion(fileName);
}
@@ -410,8 +421,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);
}
@@ -941,6 +960,7 @@ namespace ts {
if (result.error) {
return {
options: {},
typingOptions: {},
files: [],
errors: [realizeDiagnostic(result.error, "\r\n")]
};
@@ -951,6 +971,7 @@ namespace ts {
return {
options: configFile.options,
typingOptions: configFile.typingOptions,
files: configFile.fileNames,
errors: realizeDiagnostics(configFile.errors, "\r\n")
};
@@ -963,6 +984,21 @@ namespace ts {
() => getDefaultCompilerOptions()
);
}
public discoverTypings(discoverTypingsJson: string): string {
const getCanonicalFileName = createGetCanonicalFileName(/*useCaseSensitivefileNames:*/ false);
return this.forwardJSONCall("discoverTypings()", () => {
const info = <DiscoverTypingsInfo>JSON.parse(discoverTypingsJson);
return ts.JsTyping.discoverTypings(
this.host,
info.fileNames,
toPath(info.projectRootPath, info.projectRootPath, getCanonicalFileName),
toPath(info.safeListPath, info.safeListPath, getCanonicalFileName),
info.packageNameToTypingLocation,
info.typingOptions,
info.compilerOptions);
});
}
}
export class TypeScriptServicesFactory implements ShimFactory {
+1
View File
@@ -30,6 +30,7 @@
"shims.ts",
"signatureHelp.ts",
"utilities.ts",
"jsTyping.ts",
"formatting/formatting.ts",
"formatting/formattingContext.ts",
"formatting/formattingRequestKind.ts",
+15
View File
@@ -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);
}
}