This commit is contained in:
Ryan Cavanaugh 2017-08-22 14:31:49 -07:00
Родитель e83a8ea2fc 009d9b4f22
Коммит 43b8ce664c
4185 изменённых файлов: 245523 добавлений и 221996 удалений

Просмотреть файл

@ -16,4 +16,5 @@ Jakefile.js
.gitattributes
.settings/
.travis.yml
.vscode/
.vscode/
test.config

Просмотреть файл

@ -16,6 +16,7 @@ matrix:
branches:
only:
- master
- release-2.5
install:
- npm uninstall typescript --no-save

7
.vscode/tasks.json поставляемый
Просмотреть файл

@ -18,6 +18,13 @@
"problemMatcher": [
"$tsc"
]
},
{
"taskName": "tests",
"showOutput": "silent",
"problemMatcher": [
"$tsc"
]
}
]
}

Просмотреть файл

@ -28,7 +28,6 @@ import minimist = require("minimist");
import browserify = require("browserify");
import through2 = require("through2");
import merge2 = require("merge2");
import intoStream = require("into-stream");
import * as os from "os";
import fold = require("travis-fold");
const gulp = helpMaker(originalGulp);
@ -747,50 +746,75 @@ gulp.task(nodeServerOutFile, /*help*/ false, [servicesFile], () => {
import convertMap = require("convert-source-map");
import sorcery = require("sorcery");
declare module "convert-source-map" {
export function fromSource(source: string, largeSource?: boolean): SourceMapConverter;
}
import Vinyl = require("vinyl");
gulp.task("browserify", "Runs browserify on run.js to produce a file suitable for running tests in the browser", [servicesFile, run], (done) => {
const testProject = tsc.createProject("src/harness/tsconfig.json", getCompilerSettings({ outFile: "../../built/local/bundle.js" }, /*useBuiltCompiler*/ true));
return testProject.src()
.pipe(newer("built/local/bundle.js"))
const bundlePath = path.resolve("built/local/bundle.js");
gulp.task("browserify", "Runs browserify on run.js to produce a file suitable for running tests in the browser", [servicesFile], (done) => {
const testProject = tsc.createProject("src/harness/tsconfig.json", getCompilerSettings({ outFile: bundlePath, inlineSourceMap: true }, /*useBuiltCompiler*/ true));
let originalMap: any;
let prebundledContent: string;
browserify(testProject.src()
.pipe(newer(bundlePath))
.pipe(sourcemaps.init())
.pipe(testProject())
.pipe(through2.obj((file, enc, next) => {
const originalMap = file.sourceMap;
const prebundledContent = file.contents.toString();
if (originalMap) {
throw new Error("Should only recieve one file!");
}
console.log(`Saving sourcemaps for ${file.path}`);
originalMap = file.sourceMap;
prebundledContent = file.contents.toString();
// Make paths absolute to help sorcery deal with all the terrible paths being thrown around
originalMap.sources = originalMap.sources.map(s => path.resolve(path.join("src/harness", s)));
// intoStream (below) makes browserify think the input file is named this, so this is what it puts in the sourcemap
// browserify names input files this when they are streamed in, so this is what it puts in the sourcemap
originalMap.file = "built/local/_stream_0.js";
browserify(intoStream(file.contents), { debug: true })
.bundle((err, res) => {
// assumes file.contents is a Buffer
const maps = JSON.parse(convertMap.fromSource(res.toString(), /*largeSource*/ true).toJSON());
delete maps.sourceRoot;
maps.sources = maps.sources.map(s => path.resolve(s === "_stream_0.js" ? "built/local/_stream_0.js" : s));
// Strip browserify's inline comments away (could probably just let sorcery do this, but then we couldn't fix the paths)
file.contents = new Buffer(convertMap.removeComments(res.toString()));
const chain = sorcery.loadSync("built/local/bundle.js", {
content: {
"built/local/_stream_0.js": prebundledContent,
"built/local/bundle.js": file.contents.toString()
},
sourcemaps: {
"built/local/_stream_0.js": originalMap,
"built/local/bundle.js": maps,
"node_modules/source-map-support/source-map-support.js": undefined,
}
});
const finalMap = chain.apply();
file.sourceMap = finalMap;
next(/*err*/ undefined, file);
});
next(/*err*/ undefined, file.contents);
}))
.pipe(sourcemaps.write(".", { includeContent: false }))
.pipe(gulp.dest("src/harness"));
.on("error", err => {
return done(err);
}), { debug: true, basedir: __dirname }) // Attach error handler to inner stream
.bundle((err, contents) => {
if (err) {
if (err.message.match(/Cannot find module '.*_stream_0.js'/)) {
return done(); // Browserify errors when we pass in no files when `newer` filters the input, we should count that as a success, though
}
return done(err);
}
const stringContent = contents.toString();
const file = new Vinyl({ contents, path: bundlePath });
console.log(`Fixing sourcemaps for ${file.path}`);
// assumes contents is a Buffer, since that's what browserify yields
const maps = convertMap.fromSource(stringContent, /*largeSource*/ true).toObject();
delete maps.sourceRoot;
maps.sources = maps.sources.map(s => path.resolve(s === "_stream_0.js" ? "built/local/_stream_0.js" : s));
// Strip browserify's inline comments away (could probably just let sorcery do this, but then we couldn't fix the paths)
file.contents = new Buffer(convertMap.removeComments(stringContent));
const chain = sorcery.loadSync(bundlePath, {
content: {
"built/local/_stream_0.js": prebundledContent,
[bundlePath]: stringContent
},
sourcemaps: {
"built/local/_stream_0.js": originalMap,
[bundlePath]: maps,
"node_modules/source-map-support/source-map-support.js": undefined,
}
});
const finalMap = chain.apply();
file.sourceMap = finalMap;
const stream = through2.obj((file, enc, callback) => {
return callback(/*err*/ undefined, file);
});
stream.pipe(sourcemaps.write(".", { includeContent: false }))
.pipe(gulp.dest("."))
.on("end", done)
.on("error", done);
stream.write(file);
stream.end();
});
});

Просмотреть файл

@ -135,6 +135,7 @@ var harnessSources = harnessCoreSources.concat([
"projectErrors.ts",
"matchFiles.ts",
"initializeTSConfig.ts",
"extractMethods.ts",
"printer.ts",
"textChanges.ts",
"telemetry.ts",
@ -536,7 +537,6 @@ var tscFile = path.join(builtLocalDirectory, compilerFilename);
compileFile(tscFile, compilerSources, [builtLocalDirectory, copyright].concat(compilerSources), [copyright], /*useBuiltCompiler:*/ false);
var servicesFile = path.join(builtLocalDirectory, "typescriptServices.js");
var servicesFileInBrowserTest = path.join(builtLocalDirectory, "typescriptServicesInBrowserTest.js");
var standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts");
var nodePackageFile = path.join(builtLocalDirectory, "typescript.js");
var nodeDefinitionsFile = path.join(builtLocalDirectory, "typescript.d.ts");
@ -575,22 +575,6 @@ compileFile(servicesFile, servicesSources, [builtLocalDirectory, copyright].conc
fs.writeFileSync(nodeStandaloneDefinitionsFile, nodeStandaloneDefinitionsFileContents);
});
compileFile(
servicesFileInBrowserTest,
servicesSources,
[builtLocalDirectory, copyright].concat(servicesSources),
/*prefixes*/[copyright],
/*useBuiltCompiler*/ true,
{
noOutFile: false,
generateDeclarations: true,
preserveConstEnums: true,
keepComments: true,
noResolve: false,
stripInternal: true,
inlineSourceMap: true
});
file(typescriptServicesDts, [servicesFile]);
var cancellationTokenFile = path.join(builtLocalDirectory, "cancellationToken.js");
@ -737,7 +721,7 @@ compileFile(
/*prereqs*/[builtLocalDirectory, tscFile].concat(libraryTargets).concat(servicesSources).concat(harnessSources),
/*prefixes*/[],
/*useBuiltCompiler:*/ true,
/*opts*/ { inlineSourceMap: true, types: ["node", "mocha", "chai"], lib: "es6" });
/*opts*/ { types: ["node", "mocha", "chai"], lib: "es6" });
var internalTests = "internal/";
@ -973,13 +957,14 @@ var nodeServerInFile = "tests/webTestServer.ts";
compileFile(nodeServerOutFile, [nodeServerInFile], [builtLocalDirectory, tscFile], [], /*useBuiltCompiler:*/ true, { noOutFile: true, lib: "es6" });
desc("Runs browserify on run.js to produce a file suitable for running tests in the browser");
task("browserify", ["tests", run, builtLocalDirectory, nodeServerOutFile], function() {
var cmd = 'browserify built/local/run.js -t ./scripts/browserify-optional -d -o built/local/bundle.js';
task("browserify", [], function() {
// Shell out to `gulp`, since we do the work to handle sourcemaps correctly w/o inline maps there
var cmd = 'gulp browserify --silent';
exec(cmd);
}, { async: true });
desc("Runs the tests using the built run.js file like 'jake runtests'. Syntax is jake runtests-browser. Additional optional parameters tests=[regex], browser=[chrome|IE]");
task("runtests-browser", ["tests", "browserify", builtLocalDirectory, servicesFileInBrowserTest], function () {
task("runtests-browser", ["browserify", nodeServerOutFile], function () {
cleanTestDirs();
host = "node";
browser = process.env.browser || process.env.b || (os.platform() === "linux" ? "chrome" : "IE");
@ -1134,14 +1119,15 @@ task("update-sublime", ["local", serverFile], function () {
var tslintRuleDir = "scripts/tslint";
var tslintRules = [
"nextLineRule",
"booleanTriviaRule",
"typeOperatorSpacingRule",
"noInOperatorRule",
"debugAssertRule",
"nextLineRule",
"noBomRule",
"noIncrementDecrementRule",
"objectLiteralSurroundingSpaceRule",
"noInOperatorRule",
"noTypeAssertionWhitespaceRule",
"noBomRule"
"objectLiteralSurroundingSpaceRule",
"typeOperatorSpacingRule",
];
var tslintRulesFiles = tslintRules.map(function (p) {
return path.join(tslintRuleDir, p + ".ts");

Просмотреть файл

@ -69,5 +69,3 @@ function createCancellationToken(args) {
}
}
module.exports = createCancellationToken;
//# sourceMappingURL=cancellationToken.js.map

9149
lib/lib.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

8652
lib/lib.dom.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

2
lib/lib.es2015.d.ts поставляемый
Просмотреть файл

@ -21,8 +21,8 @@ and limitations under the License.
/// <reference path="lib.es2015.core.d.ts" />
/// <reference path="lib.es2015.collection.d.ts" />
/// <reference path="lib.es2015.generator.d.ts" />
/// <reference path="lib.es2015.iterable.d.ts" />
/// <reference path="lib.es2015.promise.d.ts" />
/// <reference path="lib.es2015.iterable.d.ts" />
/// <reference path="lib.es2015.proxy.d.ts" />
/// <reference path="lib.es2015.reflect.d.ts" />
/// <reference path="lib.es2015.symbol.d.ts" />

10476
lib/lib.es2016.full.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

2
lib/lib.es2017.d.ts поставляемый
Просмотреть файл

@ -22,4 +22,4 @@ and limitations under the License.
/// <reference path="lib.es2017.object.d.ts" />
/// <reference path="lib.es2017.sharedmemory.d.ts" />
/// <reference path="lib.es2017.string.d.ts" />
/// <reference path="lib.es2017.intl.d.ts" />
/// <reference path="lib.es2017.intl.d.ts" />

10475
lib/lib.es2017.full.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

30
lib/lib.es2017.intl.d.ts поставляемый Normal file
Просмотреть файл

@ -0,0 +1,30 @@
/*! *****************************************************************************
Copyright (c) Microsoft Corporation. All rights reserved.
Licensed under the Apache License, Version 2.0 (the "License"); you may not use
this file except in compliance with the License. You may obtain a copy of the
License at http://www.apache.org/licenses/LICENSE-2.0
THIS CODE IS PROVIDED ON AN *AS IS* BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION ANY IMPLIED
WARRANTIES OR CONDITIONS OF TITLE, FITNESS FOR A PARTICULAR PURPOSE,
MERCHANTABLITY OR NON-INFRINGEMENT.
See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
/// <reference no-default-lib="true"/>
type DateTimeFormatPartTypes = "day" | "dayPeriod" | "era" | "hour" | "literal" | "minute" | "month" | "second" | "timeZoneName" | "weekday" | "year";
interface DateTimeFormatPart {
type: DateTimeFormatPartTypes;
value: string;
}
interface DateTimeFormat {
formatToParts(date?: Date | number): DateTimeFormatPart[];
}

497
lib/lib.es5.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

9149
lib/lib.es6.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

10476
lib/lib.esnext.full.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

279
lib/lib.webworker.d.ts поставляемый
Просмотреть файл

@ -20,7 +20,7 @@ and limitations under the License.
/////////////////////////////
/// IE Worker APIs
/// Worker APIs
/////////////////////////////
interface Algorithm {
@ -28,16 +28,16 @@ interface Algorithm {
}
interface CacheQueryOptions {
ignoreSearch?: boolean;
ignoreMethod?: boolean;
ignoreVary?: boolean;
cacheName?: string;
ignoreMethod?: boolean;
ignoreSearch?: boolean;
ignoreVary?: boolean;
}
interface CloseEventInit extends EventInit {
wasClean?: boolean;
code?: number;
reason?: string;
wasClean?: boolean;
}
interface EventInit {
@ -69,16 +69,16 @@ interface MessageEventInit extends EventInit {
channel?: string;
data?: any;
origin?: string;
source?: any;
ports?: MessagePort[];
source?: any;
}
interface NotificationOptions {
dir?: NotificationDirection;
lang?: string;
body?: string;
tag?: string;
dir?: NotificationDirection;
icon?: string;
lang?: string;
tag?: string;
}
interface ObjectURLOptions {
@ -86,29 +86,29 @@ interface ObjectURLOptions {
}
interface PushSubscriptionOptionsInit {
userVisibleOnly?: boolean;
applicationServerKey?: any;
userVisibleOnly?: boolean;
}
interface RequestInit {
method?: string;
headers?: any;
body?: any;
referrer?: string;
referrerPolicy?: ReferrerPolicy;
mode?: RequestMode;
credentials?: RequestCredentials;
cache?: RequestCache;
redirect?: RequestRedirect;
credentials?: RequestCredentials;
headers?: any;
integrity?: string;
keepalive?: boolean;
method?: string;
mode?: RequestMode;
redirect?: RequestRedirect;
referrer?: string;
referrerPolicy?: ReferrerPolicy;
window?: any;
}
interface ResponseInit {
headers?: any;
status?: number;
statusText?: string;
headers?: any;
}
interface ClientQueryOptions {
@ -176,7 +176,7 @@ interface AudioBuffer {
declare var AudioBuffer: {
prototype: AudioBuffer;
new(): AudioBuffer;
}
};
interface Blob {
readonly size: number;
@ -189,7 +189,7 @@ interface Blob {
declare var Blob: {
prototype: Blob;
new (blobParts?: any[], options?: BlobPropertyBag): Blob;
}
};
interface Cache {
add(request: RequestInfo): Promise<void>;
@ -204,7 +204,7 @@ interface Cache {
declare var Cache: {
prototype: Cache;
new(): Cache;
}
};
interface CacheStorage {
delete(cacheName: string): Promise<boolean>;
@ -217,7 +217,7 @@ interface CacheStorage {
declare var CacheStorage: {
prototype: CacheStorage;
new(): CacheStorage;
}
};
interface CloseEvent extends Event {
readonly code: number;
@ -229,7 +229,7 @@ interface CloseEvent extends Event {
declare var CloseEvent: {
prototype: CloseEvent;
new(typeArg: string, eventInitDict?: CloseEventInit): CloseEvent;
}
};
interface Console {
assert(test?: boolean, message?: string, ...optionalParams: any[]): void;
@ -240,8 +240,8 @@ interface Console {
dirxml(value: any): void;
error(message?: any, ...optionalParams: any[]): void;
exception(message?: string, ...optionalParams: any[]): void;
group(groupTitle?: string): void;
groupCollapsed(groupTitle?: string): void;
group(groupTitle?: string, ...optionalParams: any[]): void;
groupCollapsed(groupTitle?: string, ...optionalParams: any[]): void;
groupEnd(): void;
info(message?: any, ...optionalParams: any[]): void;
log(message?: any, ...optionalParams: any[]): void;
@ -259,7 +259,7 @@ interface Console {
declare var Console: {
prototype: Console;
new(): Console;
}
};
interface Coordinates {
readonly accuracy: number;
@ -274,7 +274,7 @@ interface Coordinates {
declare var Coordinates: {
prototype: Coordinates;
new(): Coordinates;
}
};
interface CryptoKey {
readonly algorithm: KeyAlgorithm;
@ -286,7 +286,7 @@ interface CryptoKey {
declare var CryptoKey: {
prototype: CryptoKey;
new(): CryptoKey;
}
};
interface DOMError {
readonly name: string;
@ -296,7 +296,7 @@ interface DOMError {
declare var DOMError: {
prototype: DOMError;
new(): DOMError;
}
};
interface DOMException {
readonly code: number;
@ -316,10 +316,10 @@ interface DOMException {
readonly INVALID_STATE_ERR: number;
readonly NAMESPACE_ERR: number;
readonly NETWORK_ERR: number;
readonly NOT_FOUND_ERR: number;
readonly NOT_SUPPORTED_ERR: number;
readonly NO_DATA_ALLOWED_ERR: number;
readonly NO_MODIFICATION_ALLOWED_ERR: number;
readonly NOT_FOUND_ERR: number;
readonly NOT_SUPPORTED_ERR: number;
readonly PARSE_ERR: number;
readonly QUOTA_EXCEEDED_ERR: number;
readonly SECURITY_ERR: number;
@ -348,10 +348,10 @@ declare var DOMException: {
readonly INVALID_STATE_ERR: number;
readonly NAMESPACE_ERR: number;
readonly NETWORK_ERR: number;
readonly NOT_FOUND_ERR: number;
readonly NOT_SUPPORTED_ERR: number;
readonly NO_DATA_ALLOWED_ERR: number;
readonly NO_MODIFICATION_ALLOWED_ERR: number;
readonly NOT_FOUND_ERR: number;
readonly NOT_SUPPORTED_ERR: number;
readonly PARSE_ERR: number;
readonly QUOTA_EXCEEDED_ERR: number;
readonly SECURITY_ERR: number;
@ -362,7 +362,7 @@ declare var DOMException: {
readonly URL_MISMATCH_ERR: number;
readonly VALIDATION_ERR: number;
readonly WRONG_DOCUMENT_ERR: number;
}
};
interface DOMStringList {
readonly length: number;
@ -374,7 +374,7 @@ interface DOMStringList {
declare var DOMStringList: {
prototype: DOMStringList;
new(): DOMStringList;
}
};
interface ErrorEvent extends Event {
readonly colno: number;
@ -388,12 +388,12 @@ interface ErrorEvent extends Event {
declare var ErrorEvent: {
prototype: ErrorEvent;
new(type: string, errorEventInitDict?: ErrorEventInit): ErrorEvent;
}
};
interface Event {
readonly bubbles: boolean;
cancelBubble: boolean;
readonly cancelable: boolean;
cancelBubble: boolean;
readonly currentTarget: EventTarget;
readonly defaultPrevented: boolean;
readonly eventPhase: number;
@ -420,7 +420,7 @@ declare var Event: {
readonly AT_TARGET: number;
readonly BUBBLING_PHASE: number;
readonly CAPTURING_PHASE: number;
}
};
interface EventTarget {
addEventListener(type: string, listener?: EventListenerOrEventListenerObject, options?: boolean | AddEventListenerOptions): void;
@ -431,7 +431,7 @@ interface EventTarget {
declare var EventTarget: {
prototype: EventTarget;
new(): EventTarget;
}
};
interface File extends Blob {
readonly lastModifiedDate: any;
@ -442,7 +442,7 @@ interface File extends Blob {
declare var File: {
prototype: File;
new (parts: (ArrayBuffer | ArrayBufferView | Blob | string)[], filename: string, properties?: FilePropertyBag): File;
}
};
interface FileList {
readonly length: number;
@ -453,7 +453,7 @@ interface FileList {
declare var FileList: {
prototype: FileList;
new(): FileList;
}
};
interface FileReader extends EventTarget, MSBaseReader {
readonly error: DOMError;
@ -468,8 +468,17 @@ interface FileReader extends EventTarget, MSBaseReader {
declare var FileReader: {
prototype: FileReader;
new(): FileReader;
};
interface FormData {
append(name: string, value: string | Blob, fileName?: string): void;
}
declare var FormData: {
prototype: FormData;
new(): FormData;
};
interface Headers {
append(name: string, value: string): void;
delete(name: string): void;
@ -482,7 +491,7 @@ interface Headers {
declare var Headers: {
prototype: Headers;
new(init?: any): Headers;
}
};
interface IDBCursor {
readonly direction: IDBCursorDirection;
@ -506,7 +515,7 @@ declare var IDBCursor: {
readonly NEXT_NO_DUPLICATE: string;
readonly PREV: string;
readonly PREV_NO_DUPLICATE: string;
}
};
interface IDBCursorWithValue extends IDBCursor {
readonly value: any;
@ -515,7 +524,7 @@ interface IDBCursorWithValue extends IDBCursor {
declare var IDBCursorWithValue: {
prototype: IDBCursorWithValue;
new(): IDBCursorWithValue;
}
};
interface IDBDatabaseEventMap {
"abort": Event;
@ -532,7 +541,7 @@ interface IDBDatabase extends EventTarget {
close(): void;
createObjectStore(name: string, optionalParameters?: IDBObjectStoreParameters): IDBObjectStore;
deleteObjectStore(name: string): void;
transaction(storeNames: string | string[], mode?: string): IDBTransaction;
transaction(storeNames: string | string[], mode?: IDBTransactionMode): IDBTransaction;
addEventListener(type: "versionchange", listener: (ev: IDBVersionChangeEvent) => any, useCapture?: boolean): void;
addEventListener<K extends keyof IDBDatabaseEventMap>(type: K, listener: (this: IDBDatabase, ev: IDBDatabaseEventMap[K]) => any, useCapture?: boolean): void;
addEventListener(type: string, listener: EventListenerOrEventListenerObject, useCapture?: boolean): void;
@ -541,7 +550,7 @@ interface IDBDatabase extends EventTarget {
declare var IDBDatabase: {
prototype: IDBDatabase;
new(): IDBDatabase;
}
};
interface IDBFactory {
cmp(first: any, second: any): number;
@ -552,7 +561,7 @@ interface IDBFactory {
declare var IDBFactory: {
prototype: IDBFactory;
new(): IDBFactory;
}
};
interface IDBIndex {
keyPath: string | string[];
@ -563,14 +572,14 @@ interface IDBIndex {
count(key?: IDBKeyRange | IDBValidKey): IDBRequest;
get(key: IDBKeyRange | IDBValidKey): IDBRequest;
getKey(key: IDBKeyRange | IDBValidKey): IDBRequest;
openCursor(range?: IDBKeyRange | IDBValidKey, direction?: string): IDBRequest;
openKeyCursor(range?: IDBKeyRange | IDBValidKey, direction?: string): IDBRequest;
openCursor(range?: IDBKeyRange | IDBValidKey, direction?: IDBCursorDirection): IDBRequest;
openKeyCursor(range?: IDBKeyRange | IDBValidKey, direction?: IDBCursorDirection): IDBRequest;
}
declare var IDBIndex: {
prototype: IDBIndex;
new(): IDBIndex;
}
};
interface IDBKeyRange {
readonly lower: any;
@ -586,7 +595,7 @@ declare var IDBKeyRange: {
lowerBound(lower: any, open?: boolean): IDBKeyRange;
only(value: any): IDBKeyRange;
upperBound(upper: any, open?: boolean): IDBKeyRange;
}
};
interface IDBObjectStore {
readonly indexNames: DOMStringList;
@ -602,14 +611,14 @@ interface IDBObjectStore {
deleteIndex(indexName: string): void;
get(key: any): IDBRequest;
index(name: string): IDBIndex;
openCursor(range?: IDBKeyRange | IDBValidKey, direction?: string): IDBRequest;
openCursor(range?: IDBKeyRange | IDBValidKey, direction?: IDBCursorDirection): IDBRequest;
put(value: any, key?: IDBKeyRange | IDBValidKey): IDBRequest;
}
declare var IDBObjectStore: {
prototype: IDBObjectStore;
new(): IDBObjectStore;
}
};
interface IDBOpenDBRequestEventMap extends IDBRequestEventMap {
"blocked": Event;
@ -626,7 +635,7 @@ interface IDBOpenDBRequest extends IDBRequest {
declare var IDBOpenDBRequest: {
prototype: IDBOpenDBRequest;
new(): IDBOpenDBRequest;
}
};
interface IDBRequestEventMap {
"error": Event;
@ -634,7 +643,7 @@ interface IDBRequestEventMap {
}
interface IDBRequest extends EventTarget {
readonly error: DOMError;
readonly error: DOMException;
onerror: (this: IDBRequest, ev: Event) => any;
onsuccess: (this: IDBRequest, ev: Event) => any;
readonly readyState: IDBRequestReadyState;
@ -648,7 +657,7 @@ interface IDBRequest extends EventTarget {
declare var IDBRequest: {
prototype: IDBRequest;
new(): IDBRequest;
}
};
interface IDBTransactionEventMap {
"abort": Event;
@ -658,7 +667,7 @@ interface IDBTransactionEventMap {
interface IDBTransaction extends EventTarget {
readonly db: IDBDatabase;
readonly error: DOMError;
readonly error: DOMException;
readonly mode: IDBTransactionMode;
onabort: (this: IDBTransaction, ev: Event) => any;
oncomplete: (this: IDBTransaction, ev: Event) => any;
@ -678,7 +687,7 @@ declare var IDBTransaction: {
readonly READ_ONLY: string;
readonly READ_WRITE: string;
readonly VERSION_CHANGE: string;
}
};
interface IDBVersionChangeEvent extends Event {
readonly newVersion: number | null;
@ -688,7 +697,7 @@ interface IDBVersionChangeEvent extends Event {
declare var IDBVersionChangeEvent: {
prototype: IDBVersionChangeEvent;
new(): IDBVersionChangeEvent;
}
};
interface ImageData {
data: Uint8ClampedArray;
@ -700,7 +709,7 @@ declare var ImageData: {
prototype: ImageData;
new(width: number, height: number): ImageData;
new(array: Uint8ClampedArray, width: number, height: number): ImageData;
}
};
interface MessageChannel {
readonly port1: MessagePort;
@ -710,7 +719,7 @@ interface MessageChannel {
declare var MessageChannel: {
prototype: MessageChannel;
new(): MessageChannel;
}
};
interface MessageEvent extends Event {
readonly data: any;
@ -723,7 +732,7 @@ interface MessageEvent extends Event {
declare var MessageEvent: {
prototype: MessageEvent;
new(type: string, eventInitDict?: MessageEventInit): MessageEvent;
}
};
interface MessagePortEventMap {
"message": MessageEvent;
@ -741,7 +750,7 @@ interface MessagePort extends EventTarget {
declare var MessagePort: {
prototype: MessagePort;
new(): MessagePort;
}
};
interface NotificationEventMap {
"click": Event;
@ -771,7 +780,7 @@ declare var Notification: {
prototype: Notification;
new(title: string, options?: NotificationOptions): Notification;
requestPermission(callback?: NotificationPermissionCallback): Promise<NotificationPermission>;
}
};
interface Performance {
readonly navigation: PerformanceNavigation;
@ -794,7 +803,7 @@ interface Performance {
declare var Performance: {
prototype: Performance;
new(): Performance;
}
};
interface PerformanceNavigation {
readonly redirectCount: number;
@ -813,18 +822,18 @@ declare var PerformanceNavigation: {
readonly TYPE_NAVIGATE: number;
readonly TYPE_RELOAD: number;
readonly TYPE_RESERVED: number;
}
};
interface PerformanceTiming {
readonly connectEnd: number;
readonly connectStart: number;
readonly domainLookupEnd: number;
readonly domainLookupStart: number;
readonly domComplete: number;
readonly domContentLoadedEventEnd: number;
readonly domContentLoadedEventStart: number;
readonly domInteractive: number;
readonly domLoading: number;
readonly domainLookupEnd: number;
readonly domainLookupStart: number;
readonly fetchStart: number;
readonly loadEventEnd: number;
readonly loadEventStart: number;
@ -844,7 +853,7 @@ interface PerformanceTiming {
declare var PerformanceTiming: {
prototype: PerformanceTiming;
new(): PerformanceTiming;
}
};
interface Position {
readonly coords: Coordinates;
@ -854,7 +863,7 @@ interface Position {
declare var Position: {
prototype: Position;
new(): Position;
}
};
interface PositionError {
readonly code: number;
@ -871,7 +880,7 @@ declare var PositionError: {
readonly PERMISSION_DENIED: number;
readonly POSITION_UNAVAILABLE: number;
readonly TIMEOUT: number;
}
};
interface ProgressEvent extends Event {
readonly lengthComputable: boolean;
@ -883,7 +892,7 @@ interface ProgressEvent extends Event {
declare var ProgressEvent: {
prototype: ProgressEvent;
new(type: string, eventInitDict?: ProgressEventInit): ProgressEvent;
}
};
interface PushManager {
getSubscription(): Promise<PushSubscription>;
@ -894,7 +903,7 @@ interface PushManager {
declare var PushManager: {
prototype: PushManager;
new(): PushManager;
}
};
interface PushSubscription {
readonly endpoint: USVString;
@ -907,7 +916,7 @@ interface PushSubscription {
declare var PushSubscription: {
prototype: PushSubscription;
new(): PushSubscription;
}
};
interface PushSubscriptionOptions {
readonly applicationServerKey: ArrayBuffer | null;
@ -917,7 +926,7 @@ interface PushSubscriptionOptions {
declare var PushSubscriptionOptions: {
prototype: PushSubscriptionOptions;
new(): PushSubscriptionOptions;
}
};
interface ReadableStream {
readonly locked: boolean;
@ -928,7 +937,7 @@ interface ReadableStream {
declare var ReadableStream: {
prototype: ReadableStream;
new(): ReadableStream;
}
};
interface ReadableStreamReader {
cancel(): Promise<void>;
@ -939,7 +948,7 @@ interface ReadableStreamReader {
declare var ReadableStreamReader: {
prototype: ReadableStreamReader;
new(): ReadableStreamReader;
}
};
interface Request extends Object, Body {
readonly cache: RequestCache;
@ -961,7 +970,7 @@ interface Request extends Object, Body {
declare var Request: {
prototype: Request;
new(input: Request | string, init?: RequestInit): Request;
}
};
interface Response extends Object, Body {
readonly body: ReadableStream | null;
@ -977,7 +986,9 @@ interface Response extends Object, Body {
declare var Response: {
prototype: Response;
new(body?: any, init?: ResponseInit): Response;
}
error: () => Response;
redirect: (url: string, status?: number) => Response;
};
interface ServiceWorkerEventMap extends AbstractWorkerEventMap {
"statechange": Event;
@ -995,7 +1006,7 @@ interface ServiceWorker extends EventTarget, AbstractWorker {
declare var ServiceWorker: {
prototype: ServiceWorker;
new(): ServiceWorker;
}
};
interface ServiceWorkerRegistrationEventMap {
"updatefound": Event;
@ -1020,7 +1031,7 @@ interface ServiceWorkerRegistration extends EventTarget {
declare var ServiceWorkerRegistration: {
prototype: ServiceWorkerRegistration;
new(): ServiceWorkerRegistration;
}
};
interface SyncManager {
getTags(): any;
@ -1030,7 +1041,7 @@ interface SyncManager {
declare var SyncManager: {
prototype: SyncManager;
new(): SyncManager;
}
};
interface URL {
hash: string;
@ -1053,7 +1064,7 @@ declare var URL: {
new(url: string, base?: string): URL;
createObjectURL(object: any, options?: ObjectURLOptions): string;
revokeObjectURL(url: string): void;
}
};
interface WebSocketEventMap {
"close": CloseEvent;
@ -1090,7 +1101,7 @@ declare var WebSocket: {
readonly CLOSING: number;
readonly CONNECTING: number;
readonly OPEN: number;
}
};
interface WorkerEventMap extends AbstractWorkerEventMap {
"message": MessageEvent;
@ -1107,7 +1118,7 @@ interface Worker extends EventTarget, AbstractWorker {
declare var Worker: {
prototype: Worker;
new(stringUrl: string): Worker;
}
};
interface XMLHttpRequestEventMap extends XMLHttpRequestEventTargetEventMap {
"readystatechange": Event;
@ -1153,7 +1164,7 @@ declare var XMLHttpRequest: {
readonly LOADING: number;
readonly OPENED: number;
readonly UNSENT: number;
}
};
interface XMLHttpRequestUpload extends EventTarget, XMLHttpRequestEventTarget {
addEventListener<K extends keyof XMLHttpRequestEventTargetEventMap>(type: K, listener: (this: XMLHttpRequestUpload, ev: XMLHttpRequestEventTargetEventMap[K]) => any, useCapture?: boolean): void;
@ -1163,7 +1174,7 @@ interface XMLHttpRequestUpload extends EventTarget, XMLHttpRequestEventTarget {
declare var XMLHttpRequestUpload: {
prototype: XMLHttpRequestUpload;
new(): XMLHttpRequestUpload;
}
};
interface AbstractWorkerEventMap {
"error": ErrorEvent;
@ -1278,7 +1289,7 @@ interface Client {
declare var Client: {
prototype: Client;
new(): Client;
}
};
interface Clients {
claim(): Promise<void>;
@ -1290,7 +1301,7 @@ interface Clients {
declare var Clients: {
prototype: Clients;
new(): Clients;
}
};
interface DedicatedWorkerGlobalScopeEventMap extends WorkerGlobalScopeEventMap {
"message": MessageEvent;
@ -1307,7 +1318,7 @@ interface DedicatedWorkerGlobalScope extends WorkerGlobalScope {
declare var DedicatedWorkerGlobalScope: {
prototype: DedicatedWorkerGlobalScope;
new(): DedicatedWorkerGlobalScope;
}
};
interface ExtendableEvent extends Event {
waitUntil(f: Promise<any>): void;
@ -1316,7 +1327,7 @@ interface ExtendableEvent extends Event {
declare var ExtendableEvent: {
prototype: ExtendableEvent;
new(type: string, eventInitDict?: ExtendableEventInit): ExtendableEvent;
}
};
interface ExtendableMessageEvent extends ExtendableEvent {
readonly data: any;
@ -1329,7 +1340,7 @@ interface ExtendableMessageEvent extends ExtendableEvent {
declare var ExtendableMessageEvent: {
prototype: ExtendableMessageEvent;
new(type: string, eventInitDict?: ExtendableMessageEventInit): ExtendableMessageEvent;
}
};
interface FetchEvent extends ExtendableEvent {
readonly clientId: string | null;
@ -1341,7 +1352,7 @@ interface FetchEvent extends ExtendableEvent {
declare var FetchEvent: {
prototype: FetchEvent;
new(type: string, eventInitDict: FetchEventInit): FetchEvent;
}
};
interface FileReaderSync {
readAsArrayBuffer(blob: Blob): any;
@ -1353,7 +1364,7 @@ interface FileReaderSync {
declare var FileReaderSync: {
prototype: FileReaderSync;
new(): FileReaderSync;
}
};
interface NotificationEvent extends ExtendableEvent {
readonly action: string;
@ -1363,7 +1374,7 @@ interface NotificationEvent extends ExtendableEvent {
declare var NotificationEvent: {
prototype: NotificationEvent;
new(type: string, eventInitDict: NotificationEventInit): NotificationEvent;
}
};
interface PushEvent extends ExtendableEvent {
readonly data: PushMessageData | null;
@ -1372,7 +1383,7 @@ interface PushEvent extends ExtendableEvent {
declare var PushEvent: {
prototype: PushEvent;
new(type: string, eventInitDict?: PushEventInit): PushEvent;
}
};
interface PushMessageData {
arrayBuffer(): ArrayBuffer;
@ -1384,7 +1395,7 @@ interface PushMessageData {
declare var PushMessageData: {
prototype: PushMessageData;
new(): PushMessageData;
}
};
interface ServiceWorkerGlobalScopeEventMap extends WorkerGlobalScopeEventMap {
"activate": ExtendableEvent;
@ -1418,7 +1429,7 @@ interface ServiceWorkerGlobalScope extends WorkerGlobalScope {
declare var ServiceWorkerGlobalScope: {
prototype: ServiceWorkerGlobalScope;
new(): ServiceWorkerGlobalScope;
}
};
interface SyncEvent extends ExtendableEvent {
readonly lastChance: boolean;
@ -1428,7 +1439,7 @@ interface SyncEvent extends ExtendableEvent {
declare var SyncEvent: {
prototype: SyncEvent;
new(type: string, init: SyncEventInit): SyncEvent;
}
};
interface WindowClient extends Client {
readonly focused: boolean;
@ -1440,7 +1451,7 @@ interface WindowClient extends Client {
declare var WindowClient: {
prototype: WindowClient;
new(): WindowClient;
}
};
interface WorkerGlobalScopeEventMap {
"error": ErrorEvent;
@ -1463,7 +1474,7 @@ interface WorkerGlobalScope extends EventTarget, WorkerUtils, WindowConsole, Glo
declare var WorkerGlobalScope: {
prototype: WorkerGlobalScope;
new(): WorkerGlobalScope;
}
};
interface WorkerLocation {
readonly hash: string;
@ -1481,7 +1492,7 @@ interface WorkerLocation {
declare var WorkerLocation: {
prototype: WorkerLocation;
new(): WorkerLocation;
}
};
interface WorkerNavigator extends Object, NavigatorID, NavigatorOnLine, NavigatorBeacon, NavigatorConcurrentHardware {
readonly hardwareConcurrency: number;
@ -1490,7 +1501,7 @@ interface WorkerNavigator extends Object, NavigatorID, NavigatorOnLine, Navigato
declare var WorkerNavigator: {
prototype: WorkerNavigator;
new(): WorkerNavigator;
}
};
interface WorkerUtils extends Object, WindowBase64 {
readonly indexedDB: IDBFactory;
@ -1533,38 +1544,38 @@ interface ImageBitmap {
interface URLSearchParams {
/**
* Appends a specified key/value pair as a new search parameter.
*/
* Appends a specified key/value pair as a new search parameter.
*/
append(name: string, value: string): void;
/**
* Deletes the given search parameter, and its associated value, from the list of all search parameters.
*/
* Deletes the given search parameter, and its associated value, from the list of all search parameters.
*/
delete(name: string): void;
/**
* Returns the first value associated to the given search parameter.
*/
* Returns the first value associated to the given search parameter.
*/
get(name: string): string | null;
/**
* Returns all the values association with a given search parameter.
*/
* Returns all the values association with a given search parameter.
*/
getAll(name: string): string[];
/**
* Returns a Boolean indicating if such a search parameter exists.
*/
* Returns a Boolean indicating if such a search parameter exists.
*/
has(name: string): boolean;
/**
* Sets the value associated to a given search parameter to the given value. If there were several values, delete the others.
*/
* Sets the value associated to a given search parameter to the given value. If there were several values, delete the others.
*/
set(name: string, value: string): void;
}
declare var URLSearchParams: {
prototype: URLSearchParams;
/**
* Constructor returning a URLSearchParams object.
*/
* Constructor returning a URLSearchParams object.
*/
new (init?: string | URLSearchParams): URLSearchParams;
}
};
interface BlobPropertyBag {
type?: string;
@ -1771,8 +1782,23 @@ interface AddEventListenerOptions extends EventListenerOptions {
declare type EventListenerOrEventListenerObject = EventListener | EventListenerObject;
interface DecodeErrorCallback {
(error: DOMException): void;
}
interface DecodeSuccessCallback {
(decodedData: AudioBuffer): void;
}
interface ErrorEventHandler {
(message: string, filename?: string, lineno?: number, colno?: number, error?:Error): void;
(message: string, filename?: string, lineno?: number, colno?: number, error?: Error): void;
}
interface ForEachCallback {
(keyId: any, status: MediaKeyStatus): void;
}
interface FunctionStringCallback {
(data: string): void;
}
interface NotificationPermissionCallback {
(permission: NotificationPermission): void;
}
interface PositionCallback {
(position: Position): void;
@ -1780,21 +1806,6 @@ interface PositionCallback {
interface PositionErrorCallback {
(error: PositionError): void;
}
interface DecodeSuccessCallback {
(decodedData: AudioBuffer): void;
}
interface DecodeErrorCallback {
(error: DOMException): void;
}
interface FunctionStringCallback {
(data: string): void;
}
interface ForEachCallback {
(keyId: any, status: MediaKeyStatus): void;
}
interface NotificationPermissionCallback {
(permission: NotificationPermission): void;
}
declare var onmessage: (this: DedicatedWorkerGlobalScope, ev: MessageEvent) => any;
declare function close(): void;
declare function postMessage(message: any, transfer?: any[]): void;

275
lib/protocol.d.ts поставляемый
Просмотреть файл

@ -2,51 +2,53 @@
* Declaration module describing the TypeScript Server protocol
*/
declare namespace ts.server.protocol {
namespace CommandTypes {
type Brace = "brace";
type BraceCompletion = "braceCompletion";
type Change = "change";
type Close = "close";
type Completions = "completions";
type CompletionDetails = "completionEntryDetails";
type CompileOnSaveAffectedFileList = "compileOnSaveAffectedFileList";
type CompileOnSaveEmitFile = "compileOnSaveEmitFile";
type Configure = "configure";
type Definition = "definition";
type Implementation = "implementation";
type Exit = "exit";
type Format = "format";
type Formatonkey = "formatonkey";
type Geterr = "geterr";
type GeterrForProject = "geterrForProject";
type SemanticDiagnosticsSync = "semanticDiagnosticsSync";
type SyntacticDiagnosticsSync = "syntacticDiagnosticsSync";
type NavBar = "navbar";
type Navto = "navto";
type NavTree = "navtree";
type NavTreeFull = "navtree-full";
type Occurrences = "occurrences";
type DocumentHighlights = "documentHighlights";
type Open = "open";
type Quickinfo = "quickinfo";
type References = "references";
type Reload = "reload";
type Rename = "rename";
type Saveto = "saveto";
type SignatureHelp = "signatureHelp";
type TypeDefinition = "typeDefinition";
type ProjectInfo = "projectInfo";
type ReloadProjects = "reloadProjects";
type Unknown = "unknown";
type OpenExternalProject = "openExternalProject";
type OpenExternalProjects = "openExternalProjects";
type CloseExternalProject = "closeExternalProject";
type TodoComments = "todoComments";
type Indentation = "indentation";
type DocCommentTemplate = "docCommentTemplate";
type CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects";
type GetCodeFixes = "getCodeFixes";
type GetSupportedCodeFixes = "getSupportedCodeFixes";
const enum CommandTypes {
Brace = "brace",
BraceCompletion = "braceCompletion",
Change = "change",
Close = "close",
Completions = "completions",
CompletionDetails = "completionEntryDetails",
CompileOnSaveAffectedFileList = "compileOnSaveAffectedFileList",
CompileOnSaveEmitFile = "compileOnSaveEmitFile",
Configure = "configure",
Definition = "definition",
Implementation = "implementation",
Exit = "exit",
Format = "format",
Formatonkey = "formatonkey",
Geterr = "geterr",
GeterrForProject = "geterrForProject",
SemanticDiagnosticsSync = "semanticDiagnosticsSync",
SyntacticDiagnosticsSync = "syntacticDiagnosticsSync",
NavBar = "navbar",
Navto = "navto",
NavTree = "navtree",
NavTreeFull = "navtree-full",
Occurrences = "occurrences",
DocumentHighlights = "documentHighlights",
Open = "open",
Quickinfo = "quickinfo",
References = "references",
Reload = "reload",
Rename = "rename",
Saveto = "saveto",
SignatureHelp = "signatureHelp",
TypeDefinition = "typeDefinition",
ProjectInfo = "projectInfo",
ReloadProjects = "reloadProjects",
Unknown = "unknown",
OpenExternalProject = "openExternalProject",
OpenExternalProjects = "openExternalProjects",
CloseExternalProject = "closeExternalProject",
TodoComments = "todoComments",
Indentation = "indentation",
DocCommentTemplate = "docCommentTemplate",
CompilerOptionsForInferredProjects = "compilerOptionsForInferredProjects",
GetCodeFixes = "getCodeFixes",
GetSupportedCodeFixes = "getSupportedCodeFixes",
GetApplicableRefactors = "getApplicableRefactors",
GetEditsForRefactor = "getEditsForRefactor",
}
/**
* A TypeScript Server message
@ -287,6 +289,85 @@ declare namespace ts.server.protocol {
*/
offset: number;
}
type FileLocationOrRangeRequestArgs = FileLocationRequestArgs | FileRangeRequestArgs;
/**
* Request refactorings at a given position or selection area.
*/
interface GetApplicableRefactorsRequest extends Request {
command: CommandTypes.GetApplicableRefactors;
arguments: GetApplicableRefactorsRequestArgs;
}
type GetApplicableRefactorsRequestArgs = FileLocationOrRangeRequestArgs;
/**
* Response is a list of available refactorings.
* Each refactoring exposes one or more "Actions"; a user selects one action to invoke a refactoring
*/
interface GetApplicableRefactorsResponse extends Response {
body?: ApplicableRefactorInfo[];
}
/**
* A set of one or more available refactoring actions, grouped under a parent refactoring.
*/
interface ApplicableRefactorInfo {
/**
* The programmatic name of the refactoring
*/
name: string;
/**
* A description of this refactoring category to show to the user.
* If the refactoring gets inlined (see below), this text will not be visible.
*/
description: string;
/**
* Inlineable refactorings can have their actions hoisted out to the top level
* of a context menu. Non-inlineanable refactorings should always be shown inside
* their parent grouping.
*
* If not specified, this value is assumed to be 'true'
*/
inlineable?: boolean;
actions: RefactorActionInfo[];
}
/**
* Represents a single refactoring action - for example, the "Extract Method..." refactor might
* offer several actions, each corresponding to a surround class or closure to extract into.
*/
type RefactorActionInfo = {
/**
* The programmatic name of the refactoring action
*/
name: string;
/**
* A description of this refactoring action to show to the user.
* If the parent refactoring is inlined away, this will be the only text shown,
* so this description should make sense by itself if the parent is inlineable=true
*/
description: string;
};
interface GetEditsForRefactorRequest extends Request {
command: CommandTypes.GetEditsForRefactor;
arguments: GetEditsForRefactorRequestArgs;
}
/**
* Request the edits that a particular refactoring action produces.
* Callers must specify the name of the refactor and the name of the action.
*/
type GetEditsForRefactorRequestArgs = FileLocationOrRangeRequestArgs & {
refactor: string;
action: string;
};
interface GetEditsForRefactorResponse extends Response {
body?: RefactorEditInfo;
}
type RefactorEditInfo = {
edits: FileCodeEdits[];
/**
* An optional location where the editor should start a rename operation once
* the refactoring edits have been applied
*/
renameLocation?: Location;
renameFilename?: string;
};
/**
* Request for the available codefixes at a specific position.
*/
@ -294,10 +375,7 @@ declare namespace ts.server.protocol {
command: CommandTypes.GetCodeFixes;
arguments: CodeFixRequestArgs;
}
/**
* Instances of this interface specify errorcodes on a specific location in a sourcefile.
*/
interface CodeFixRequestArgs extends FileRequestArgs {
interface FileRangeRequestArgs extends FileRequestArgs {
/**
* The line number for the request (1-based).
*/
@ -314,6 +392,11 @@ declare namespace ts.server.protocol {
* The character offset (on the line) for the request (1-based).
*/
endOffset: number;
}
/**
* Instances of this interface specify errorcodes on a specific location in a sourcefile.
*/
interface CodeFixRequestArgs extends FileRangeRequestArgs {
/**
* Errorcodes we want to get the fixes for.
*/
@ -394,7 +477,7 @@ declare namespace ts.server.protocol {
command: CommandTypes.Implementation;
}
/**
* Location in source code expressed as (one-based) line and character offset.
* Location in source code expressed as (one-based) line and (one-based) column offset.
*/
interface Location {
line: number;
@ -488,10 +571,9 @@ declare namespace ts.server.protocol {
}
/**
* Span augmented with extra information that denotes the kind of the highlighting to be used for span.
* Kind is taken from HighlightSpanKind type.
*/
interface HighlightSpan extends TextSpan {
kind: string;
kind: HighlightSpanKind;
}
/**
* Represents a set of highligh spans for a give name
@ -609,7 +691,7 @@ declare namespace ts.server.protocol {
/**
* The items's kind (such as 'className' or 'parameterName' or plain 'text').
*/
kind: string;
kind: ScriptElementKind;
/**
* Optional modifiers for the kind (such as 'public').
*/
@ -957,7 +1039,7 @@ declare namespace ts.server.protocol {
/**
* The symbol's kind (such as 'className' or 'parameterName' or plain 'text').
*/
kind: string;
kind: ScriptElementKind;
/**
* Optional modifiers for the kind (such as 'public').
*/
@ -1143,7 +1225,7 @@ declare namespace ts.server.protocol {
/**
* The symbol's kind (such as 'className' or 'parameterName').
*/
kind: string;
kind: ScriptElementKind;
/**
* Optional modifiers for the kind (such as 'public').
*/
@ -1170,7 +1252,7 @@ declare namespace ts.server.protocol {
/**
* The symbol's kind (such as 'className' or 'parameterName').
*/
kind: string;
kind: ScriptElementKind;
/**
* Optional modifiers for the kind (such as 'public').
*/
@ -1417,6 +1499,12 @@ declare namespace ts.server.protocol {
*/
source?: string;
}
interface DiagnosticWithFileName extends Diagnostic {
/**
* Name of the file the diagnostic is in
*/
fileName: string;
}
interface DiagnosticEventBody {
/**
* The file for which diagnostic information is reported.
@ -1446,7 +1534,7 @@ declare namespace ts.server.protocol {
/**
* An arry of diagnostic information items for the found config file.
*/
diagnostics: Diagnostic[];
diagnostics: DiagnosticWithFileName[];
}
/**
* Event message for "configFileDiag" event type.
@ -1563,7 +1651,7 @@ declare namespace ts.server.protocol {
/**
* The symbol's kind (such as 'className' or 'parameterName').
*/
kind: string;
kind: ScriptElementKind;
/**
* exact, substring, or prefix.
*/
@ -1596,7 +1684,7 @@ declare namespace ts.server.protocol {
/**
* Kind of symbol's container symbol (if any).
*/
containerKind?: string;
containerKind?: ScriptElementKind;
}
/**
* Navto response message. Body is an array of navto items. Each
@ -1660,7 +1748,7 @@ declare namespace ts.server.protocol {
/**
* The symbol's kind (such as 'className' or 'parameterName').
*/
kind: string;
kind: ScriptElementKind;
/**
* Optional modifiers for the kind (such as 'public').
*/
@ -1681,7 +1769,7 @@ declare namespace ts.server.protocol {
/** protocol.NavigationTree is identical to ts.NavigationTree, except using protocol.TextSpan instead of ts.TextSpan */
interface NavigationTree {
text: string;
kind: string;
kind: ScriptElementKind;
kindModifiers: string;
spans: TextSpan[];
childItems?: NavigationTree[];
@ -1756,12 +1844,11 @@ declare namespace ts.server.protocol {
interface NavTreeResponse extends Response {
body?: NavigationTree;
}
namespace IndentStyle {
type None = "None";
type Block = "Block";
type Smart = "Smart";
const enum IndentStyle {
None = "None",
Block = "Block",
Smart = "Smart",
}
type IndentStyle = IndentStyle.None | IndentStyle.Block | IndentStyle.Smart;
interface EditorSettings {
baseIndentSize?: number;
indentSize?: number;
@ -1782,6 +1869,7 @@ declare namespace ts.server.protocol {
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces?: boolean;
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces?: boolean;
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces?: boolean;
insertSpaceAfterTypeAssertion?: boolean;
insertSpaceBeforeFunctionParenthesis?: boolean;
placeOpenBraceOnNewLineForFunctions?: boolean;
placeOpenBraceOnNewLineForControlBlocks?: boolean;
@ -1854,40 +1942,35 @@ declare namespace ts.server.protocol {
typeRoots?: string[];
[option: string]: CompilerOptionsValue | undefined;
}
namespace JsxEmit {
type None = "None";
type Preserve = "Preserve";
type ReactNative = "ReactNative";
type React = "React";
const enum JsxEmit {
None = "None",
Preserve = "Preserve",
ReactNative = "ReactNative",
React = "React",
}
type JsxEmit = JsxEmit.None | JsxEmit.Preserve | JsxEmit.React | JsxEmit.ReactNative;
namespace ModuleKind {
type None = "None";
type CommonJS = "CommonJS";
type AMD = "AMD";
type UMD = "UMD";
type System = "System";
type ES6 = "ES6";
type ES2015 = "ES2015";
const enum ModuleKind {
None = "None",
CommonJS = "CommonJS",
AMD = "AMD",
UMD = "UMD",
System = "System",
ES6 = "ES6",
ES2015 = "ES2015",
}
type ModuleKind = ModuleKind.None | ModuleKind.CommonJS | ModuleKind.AMD | ModuleKind.UMD | ModuleKind.System | ModuleKind.ES6 | ModuleKind.ES2015;
namespace ModuleResolutionKind {
type Classic = "Classic";
type Node = "Node";
const enum ModuleResolutionKind {
Classic = "Classic",
Node = "Node",
}
type ModuleResolutionKind = ModuleResolutionKind.Classic | ModuleResolutionKind.Node;
namespace NewLineKind {
type Crlf = "Crlf";
type Lf = "Lf";
const enum NewLineKind {
Crlf = "Crlf",
Lf = "Lf",
}
type NewLineKind = NewLineKind.Crlf | NewLineKind.Lf;
namespace ScriptTarget {
type ES3 = "ES3";
type ES5 = "ES5";
type ES6 = "ES6";
type ES2015 = "ES2015";
const enum ScriptTarget {
ES3 = "ES3",
ES5 = "ES5",
ES6 = "ES6",
ES2015 = "ES2015",
}
type ScriptTarget = ScriptTarget.ES3 | ScriptTarget.ES5 | ScriptTarget.ES6 | ScriptTarget.ES2015;
}
declare namespace ts.server.protocol {
@ -1943,6 +2026,8 @@ declare namespace ts.server.protocol {
}
declare namespace ts {
// these types are empty stubs for types from services and should not be used directly
export type HighlightSpanKind = never;
export type ScriptElementKind = never;
export type ScriptKind = never;
export type IndentStyle = never;
export type JsxEmit = never;

64135
lib/tsc.js

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1624
lib/tsserverlibrary.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1249
lib/typescript.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

1249
lib/typescriptServices.d.ts поставляемый

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -13,6 +13,7 @@ See the Apache Version 2.0 License for specific language governing permissions
and limitations under the License.
***************************************************************************** */
"use strict";
if (process.argv.length < 3) {
process.exit(1);
}

Просмотреть файл

@ -17,6 +17,6 @@ nodeVersions.each { nodeVer ->
}
Utilities.standardJobSetup(newJob, project, true, "*/${branch}")
Utilities.setMachineAffinity(newJob, 'Ubuntu', '20161020')
Utilities.setMachineAffinity(newJob, 'Ubuntu14.04', '20170821-1')
Utilities.addGithubPRTriggerForBranch(newJob, branch, "TypeScript Test Run ${newJobName}")
}

Просмотреть файл

@ -2,7 +2,7 @@
"name": "typescript",
"author": "Microsoft Corp.",
"homepage": "http://typescriptlang.org/",
"version": "2.5.0",
"version": "2.6.0",
"license": "Apache-2.0",
"description": "TypeScript is a language for application scale JavaScript development",
"keywords": [
@ -60,7 +60,6 @@
"gulp-newer": "latest",
"gulp-sourcemaps": "latest",
"gulp-typescript": "latest",
"into-stream": "latest",
"istanbul": "latest",
"jake": "latest",
"merge2": "latest",
@ -91,7 +90,6 @@
"setup-hooks": "node scripts/link-hooks.js"
},
"browser": {
"buffer": false,
"fs": false,
"os": false,
"path": false

Просмотреть файл

@ -7,11 +7,15 @@ function endsWith(s: string, suffix: string) {
return s.lastIndexOf(suffix, s.length - suffix.length) !== -1;
}
function isStringEnum(declaration: ts.EnumDeclaration) {
return declaration.members.length && declaration.members.every(m => m.initializer && m.initializer.kind === ts.SyntaxKind.StringLiteral);
}
class DeclarationsWalker {
private visitedTypes: ts.Type[] = [];
private text = "";
private removedTypes: ts.Type[] = [];
private constructor(private typeChecker: ts.TypeChecker, private protocolFile: ts.SourceFile) {
}
@ -19,7 +23,7 @@ class DeclarationsWalker {
let text = "declare namespace ts.server.protocol {\n";
var walker = new DeclarationsWalker(typeChecker, protocolFile);
walker.visitTypeNodes(protocolFile);
text = walker.text
text = walker.text
? `declare namespace ts.server.protocol {\n${walker.text}}`
: "";
if (walker.removedTypes) {
@ -52,7 +56,7 @@ class DeclarationsWalker {
if (sourceFile === this.protocolFile || path.basename(sourceFile.fileName) === "lib.d.ts") {
return;
}
if (decl.kind === ts.SyntaxKind.EnumDeclaration) {
if (decl.kind === ts.SyntaxKind.EnumDeclaration && !isStringEnum(decl as ts.EnumDeclaration)) {
this.removedTypes.push(type);
return;
}
@ -91,7 +95,7 @@ class DeclarationsWalker {
for (const type of heritageClauses[0].types) {
this.processTypeOfNode(type);
}
}
}
break;
}
}
@ -110,7 +114,7 @@ class DeclarationsWalker {
this.processType(type);
}
}
}
}
}
function writeProtocolFile(outputFile: string, protocolTs: string, typeScriptServicesDts: string) {

Просмотреть файл

@ -6,9 +6,7 @@ interface DiagnosticDetails {
isEarly?: boolean;
}
interface InputDiagnosticMessageTable {
[msg: string]: DiagnosticDetails;
}
type InputDiagnosticMessageTable = ts.Map<DiagnosticDetails>;
function main(): void {
var sys = ts.sys;
@ -28,101 +26,66 @@ function main(): void {
var inputFilePath = sys.args[0].replace(/\\/g, "/");
var inputStr = sys.readFile(inputFilePath);
var diagnosticMessages: InputDiagnosticMessageTable = JSON.parse(inputStr);
var diagnosticMessagesJson: { [key: string]: DiagnosticDetails } = JSON.parse(inputStr);
// Check that there are no duplicates.
const seenNames = ts.createMap<true>();
for (const name of Object.keys(diagnosticMessagesJson)) {
if (seenNames.has(name))
throw new Error(`Name ${name} appears twice`);
seenNames.set(name, true);
}
var names = Utilities.getObjectKeys(diagnosticMessages);
var nameMap = buildUniqueNameMap(names);
const diagnosticMessages: InputDiagnosticMessageTable = ts.createMapFromTemplate(diagnosticMessagesJson);
var infoFileOutput = buildInfoFileOutput(diagnosticMessages, nameMap);
checkForUniqueCodes(names, diagnosticMessages);
var infoFileOutput = buildInfoFileOutput(diagnosticMessages);
checkForUniqueCodes(diagnosticMessages);
writeFile("diagnosticInformationMap.generated.ts", infoFileOutput);
var messageOutput = buildDiagnosticMessageOutput(diagnosticMessages, nameMap);
var messageOutput = buildDiagnosticMessageOutput(diagnosticMessages);
writeFile("diagnosticMessages.generated.json", messageOutput);
}
function checkForUniqueCodes(messages: string[], diagnosticTable: InputDiagnosticMessageTable) {
const originalMessageForCode: string[] = [];
let numConflicts = 0;
for (const currentMessage of messages) {
const code = diagnosticTable[currentMessage].code;
if (code in originalMessageForCode) {
const originalMessage = originalMessageForCode[code];
ts.sys.write("\x1b[91m"); // High intensity red.
ts.sys.write("Error");
ts.sys.write("\x1b[0m"); // Reset formatting.
ts.sys.write(`: Diagnostic code '${code}' conflicts between "${originalMessage}" and "${currentMessage}".`);
ts.sys.write(ts.sys.newLine + ts.sys.newLine);
numConflicts++;
}
else {
originalMessageForCode[code] = currentMessage;
}
}
if (numConflicts > 0) {
throw new Error(`Found ${numConflicts} conflict(s) in diagnostic codes.`);
}
function checkForUniqueCodes(diagnosticTable: InputDiagnosticMessageTable) {
const allCodes: { [key: number]: true | undefined } = [];
diagnosticTable.forEach(({ code }) => {
if (allCodes[code])
throw new Error(`Diagnostic code ${code} appears more than once.`);
allCodes[code] = true;
});
}
function buildUniqueNameMap(names: string[]): ts.Map<string> {
var nameMap = ts.createMap<string>();
var uniqueNames = NameGenerator.ensureUniqueness(names, /* isCaseSensitive */ false, /* isFixed */ undefined);
for (var i = 0; i < names.length; i++) {
nameMap.set(names[i], uniqueNames[i]);
}
return nameMap;
}
function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map<string>): string {
function buildInfoFileOutput(messageTable: InputDiagnosticMessageTable): string {
var result =
'// <auto-generated />\r\n' +
'/// <reference path="types.ts" />\r\n' +
'/* @internal */\r\n' +
'namespace ts {\r\n' +
" function diag(code: number, category: DiagnosticCategory, key: string, message: string): DiagnosticMessage {\r\n" +
" return { code, category, key, message };\r\n" +
" }\r\n" +
' export const Diagnostics = {\r\n';
var names = Utilities.getObjectKeys(messageTable);
for (var i = 0; i < names.length; i++) {
var name = names[i];
var diagnosticDetails = messageTable[name];
var propName = convertPropertyName(nameMap.get(name));
result +=
' ' + propName +
': { code: ' + diagnosticDetails.code +
', category: DiagnosticCategory.' + diagnosticDetails.category +
', key: "' + createKey(propName, diagnosticDetails.code) + '"' +
', message: "' + name.replace(/[\"]/g, '\\"') + '"' +
' },\r\n';
}
messageTable.forEach(({ code, category }, name) => {
const propName = convertPropertyName(name);
result += ` ${propName}: diag(${code}, DiagnosticCategory.${category}, "${createKey(propName, code)}", ${JSON.stringify(name)}),\r\n`;
});
result += ' };\r\n}';
return result;
}
function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable, nameMap: ts.Map<string>): string {
var result =
'{';
var names = Utilities.getObjectKeys(messageTable);
for (var i = 0; i < names.length; i++) {
var name = names[i];
var diagnosticDetails = messageTable[name];
var propName = convertPropertyName(nameMap.get(name));
function buildDiagnosticMessageOutput(messageTable: InputDiagnosticMessageTable): string {
let result = '{';
messageTable.forEach(({ code }, name) => {
const propName = convertPropertyName(name);
result += `\r\n "${createKey(propName, code)}" : "${name.replace(/[\"]/g, '\\"')}",`;
});
result += '\r\n "' + createKey(propName, diagnosticDetails.code) + '"' + ' : "' + name.replace(/[\"]/g, '\\"') + '"';
if (i !== names.length - 1) {
result += ',';
}
}
// Shave trailing comma, then add newline and ending brace
result = result.slice(0, result.length - 1) + '\r\n}';
result += '\r\n}';
// Assert that we generated valid JSON
JSON.parse(result);
return result;
}
@ -139,7 +102,6 @@ function convertPropertyName(origName: string): string {
return /\w/.test(char) ? char : "_";
}).join("");
// get rid of all multi-underscores
result = result.replace(/_+/g, "_");
@ -152,93 +114,4 @@ function convertPropertyName(origName: string): string {
return result;
}
module NameGenerator {
export function ensureUniqueness(names: string[], isCaseSensitive: boolean, isFixed?: boolean[]): string[]{
if (!isFixed) {
isFixed = names.map(() => false)
}
var names = names.slice();
ensureUniquenessInPlace(names, isCaseSensitive, isFixed);
return names;
}
function ensureUniquenessInPlace(names: string[], isCaseSensitive: boolean, isFixed: boolean[]): void {
for (var i = 0; i < names.length; i++) {
var name = names[i];
var collisionIndices = Utilities.collectMatchingIndices(name, names, isCaseSensitive);
// We will always have one "collision" because getCollisionIndices returns the index of name itself as well;
// so if we only have one collision, then there are no issues.
if (collisionIndices.length < 2) {
continue;
}
handleCollisions(name, names, isFixed, collisionIndices, isCaseSensitive);
}
}
function handleCollisions(name: string, proposedNames: string[], isFixed: boolean[], collisionIndices: number[], isCaseSensitive: boolean): void {
var suffix = 1;
for (var i = 0; i < collisionIndices.length; i++) {
var collisionIndex = collisionIndices[i];
if (isFixed[collisionIndex]) {
// can't do anything about this name.
continue;
}
while (true) {
var newName = name + suffix;
suffix++;
// Check if we've synthesized a unique name, and if so
// replace the conflicting name with the new one.
if (!proposedNames.some(name => Utilities.stringEquals(name, newName, isCaseSensitive))) {
proposedNames[collisionIndex] = newName;
break;
}
}
}
}
}
module Utilities {
/// Return a list of all indices where a string occurs.
export function collectMatchingIndices(name: string, proposedNames: string[], isCaseSensitive: boolean): number[] {
var matchingIndices: number[] = [];
for (var i = 0; i < proposedNames.length; i++) {
if (stringEquals(name, proposedNames[i], isCaseSensitive)) {
matchingIndices.push(i);
}
}
return matchingIndices;
}
export function stringEquals(s1: string, s2: string, caseSensitive: boolean): boolean {
if (caseSensitive) {
s1 = s1.toLowerCase();
s2 = s2.toLowerCase();
}
return s1 == s2;
}
// Like Object.keys
export function getObjectKeys(obj: any): string[] {
var result: string[] = [];
for (var name in obj) {
if (obj.hasOwnProperty(name)) {
result.push(name);
}
}
return result;
}
}
main();

Просмотреть файл

@ -34,6 +34,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
switch (methodName) {
case "apply":
case "assert":
case "assertEqual":
case "call":
case "equal":
case "fail":
@ -69,7 +70,7 @@ function walk(ctx: Lint.WalkContext<void>): void {
const ranges = ts.getTrailingCommentRanges(sourceFile.text, arg.pos) || ts.getLeadingCommentRanges(sourceFile.text, arg.pos);
if (ranges === undefined || ranges.length !== 1 || ranges[0].kind !== ts.SyntaxKind.MultiLineCommentTrivia) {
ctx.addFailureAtNode(arg, "Tag boolean argument with parameter name");
ctx.addFailureAtNode(arg, "Tag argument with parameter name");
return;
}

Просмотреть файл

@ -0,0 +1,45 @@
import * as Lint from "tslint/lib";
import * as ts from "typescript";
export class Rule extends Lint.Rules.AbstractRule {
public apply(sourceFile: ts.SourceFile): Lint.RuleFailure[] {
return this.applyWithFunction(sourceFile, ctx => walk(ctx));
}
}
function walk(ctx: Lint.WalkContext<void>): void {
ts.forEachChild(ctx.sourceFile, function recur(node) {
if (ts.isCallExpression(node)) {
checkCall(node);
}
ts.forEachChild(node, recur);
});
function checkCall(node: ts.CallExpression) {
if (!isDebugAssert(node.expression) || node.arguments.length < 2) {
return;
}
const message = node.arguments[1];
if (!ts.isStringLiteral(message)) {
ctx.addFailureAtNode(message, "Second argument to 'Debug.assert' should be a string literal.");
}
if (node.arguments.length < 3) {
return;
}
const message2 = node.arguments[2];
if (!ts.isStringLiteral(message2) && !ts.isArrowFunction(message2)) {
ctx.addFailureAtNode(message, "Third argument to 'Debug.assert' should be a string literal or arrow function.");
}
}
function isDebugAssert(expr: ts.Node): boolean {
return ts.isPropertyAccessExpression(expr) && isName(expr.expression, "Debug") && isName(expr.name, "assert");
}
function isName(expr: ts.Node, text: string): boolean {
return ts.isIdentifier(expr) && expr.text === text;
}
}

8
scripts/types/ambient.d.ts поставляемый
Просмотреть файл

@ -13,13 +13,5 @@ declare module "gulp-insert" {
export function transform(cb: (contents: string, file: {path: string, relative: string}) => string): NodeJS.ReadWriteStream; // file is a vinyl file
}
declare module "into-stream" {
function IntoStream(content: string | Buffer | (string | Buffer)[]): NodeJS.ReadableStream;
namespace IntoStream {
export function obj(content: any): NodeJS.ReadableStream
}
export = IntoStream;
}
declare module "sorcery";
declare module "travis-fold";

Просмотреть файл

@ -806,11 +806,7 @@ namespace ts {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return <FlowCondition>{
flags,
expression,
antecedent
};
return { flags, expression, antecedent };
}
function createFlowSwitchClause(antecedent: FlowNode, switchStatement: SwitchStatement, clauseStart: number, clauseEnd: number): FlowNode {
@ -818,31 +814,18 @@ namespace ts {
return antecedent;
}
setFlowNodeReferenced(antecedent);
return <FlowSwitchClause>{
flags: FlowFlags.SwitchClause,
switchStatement,
clauseStart,
clauseEnd,
antecedent
};
return { flags: FlowFlags.SwitchClause, switchStatement, clauseStart, clauseEnd, antecedent };
}
function createFlowAssignment(antecedent: FlowNode, node: Expression | VariableDeclaration | BindingElement): FlowNode {
setFlowNodeReferenced(antecedent);
return <FlowAssignment>{
flags: FlowFlags.Assignment,
antecedent,
node
};
return { flags: FlowFlags.Assignment, antecedent, node };
}
function createFlowArrayMutation(antecedent: FlowNode, node: CallExpression | BinaryExpression): FlowNode {
setFlowNodeReferenced(antecedent);
return <FlowArrayMutation>{
flags: FlowFlags.ArrayMutation,
antecedent,
node
};
const res: FlowArrayMutation = { flags: FlowFlags.ArrayMutation, antecedent, node };
return res;
}
function finishFlowLabel(flow: FlowLabel): FlowNode {
@ -2784,7 +2767,6 @@ namespace ts {
function computeParameter(node: ParameterDeclaration, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
const modifierFlags = getModifierFlags(node);
const name = node.name;
const initializer = node.initializer;
const dotDotDotToken = node.dotDotDotToken;
@ -2799,7 +2781,7 @@ namespace ts {
}
// If a parameter has an accessibility modifier, then it is TypeScript syntax.
if (modifierFlags & ModifierFlags.ParameterPropertyModifier) {
if (hasModifier(node, ModifierFlags.ParameterPropertyModifier)) {
transformFlags |= TransformFlags.AssertTypeScript | TransformFlags.ContainsParameterPropertyAssignments;
}
@ -2844,9 +2826,8 @@ namespace ts {
function computeClassDeclaration(node: ClassDeclaration, subtreeFlags: TransformFlags) {
let transformFlags: TransformFlags;
const modifierFlags = getModifierFlags(node);
if (modifierFlags & ModifierFlags.Ambient) {
if (hasModifier(node, ModifierFlags.Ambient)) {
// An ambient declaration is TypeScript syntax.
transformFlags = TransformFlags.AssertTypeScript;
}
@ -2921,7 +2902,10 @@ namespace ts {
function computeCatchClause(node: CatchClause, subtreeFlags: TransformFlags) {
let transformFlags = subtreeFlags;
if (node.variableDeclaration && isBindingPattern(node.variableDeclaration.name)) {
if (!node.variableDeclaration) {
transformFlags |= TransformFlags.AssertESNext;
}
else if (isBindingPattern(node.variableDeclaration.name)) {
transformFlags |= TransformFlags.AssertES2015;
}
@ -3187,11 +3171,10 @@ namespace ts {
function computeVariableStatement(node: VariableStatement, subtreeFlags: TransformFlags) {
let transformFlags: TransformFlags;
const modifierFlags = getModifierFlags(node);
const declarationListTransformFlags = node.declarationList.transformFlags;
// An ambient declaration is TypeScript syntax.
if (modifierFlags & ModifierFlags.Ambient) {
if (hasModifier(node, ModifierFlags.Ambient)) {
transformFlags = TransformFlags.AssertTypeScript;
}
else {

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -340,7 +340,6 @@ namespace ts {
isTSConfigOnly: true,
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.A_series_of_entries_which_re_map_imports_to_lookup_locations_relative_to_the_baseUrl
},
{
// this option can only be specified in tsconfig.json
@ -384,6 +383,12 @@ namespace ts {
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Allow_default_imports_from_modules_with_no_default_export_This_does_not_affect_code_emit_just_typechecking
},
{
name: "preserveSymlinks",
type: "boolean",
category: Diagnostics.Module_Resolution_Options,
description: Diagnostics.Do_not_resolve_the_real_path_of_symlinks,
},
// Source Maps
{
@ -1111,7 +1116,7 @@ namespace ts {
if (option && typeof option.type !== "string") {
const customOption = <CommandLineOptionOfCustomType>option;
// Validate custom option type
if (!customOption.type.has(text)) {
if (!customOption.type.has(text.toLowerCase())) {
errors.push(
createDiagnosticForInvalidCustomType(
customOption,
@ -1446,14 +1451,10 @@ namespace ts {
}
}
else {
// If no includes were specified, exclude common package folders and the outDir
const specs = includeSpecs ? [] : ["node_modules", "bower_components", "jspm_packages"];
const outDir = raw["compilerOptions"] && raw["compilerOptions"]["outDir"];
if (outDir) {
specs.push(outDir);
excludeSpecs = [outDir];
}
excludeSpecs = specs;
}
if (fileNames === undefined && includeSpecs === undefined) {
@ -1810,7 +1811,7 @@ namespace ts {
return value;
}
else if (typeof option.type !== "string") {
return option.type.get(value);
return option.type.get(typeof value === "string" ? value.toLowerCase() : value);
}
return normalizeNonListOptionValue(option, basePath, value);
}
@ -2015,23 +2016,13 @@ namespace ts {
}
function validateSpecs(specs: ReadonlyArray<string>, errors: Push<Diagnostic>, allowTrailingRecursion: boolean, jsonSourceFile: JsonSourceFile, specKey: string) {
const validSpecs: string[] = [];
for (const spec of specs) {
if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) {
errors.push(createDiagnostic(Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec));
return specs.filter(spec => {
const diag = specToDiagnostic(spec, allowTrailingRecursion);
if (diag !== undefined) {
errors.push(createDiagnostic(diag, spec));
}
else if (invalidMultipleRecursionPatterns.test(spec)) {
errors.push(createDiagnostic(Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, spec));
}
else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) {
errors.push(createDiagnostic(Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, spec));
}
else {
validSpecs.push(spec);
}
}
return validSpecs;
return diag === undefined;
});
function createDiagnostic(message: DiagnosticMessage, spec: string): Diagnostic {
if (jsonSourceFile && jsonSourceFile.jsonObject) {
@ -2049,6 +2040,18 @@ namespace ts {
}
}
function specToDiagnostic(spec: string, allowTrailingRecursion: boolean): ts.DiagnosticMessage | undefined {
if (!allowTrailingRecursion && invalidTrailingRecursionPattern.test(spec)) {
return Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0;
}
else if (invalidMultipleRecursionPatterns.test(spec)) {
return Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0;
}
else if (invalidDotDotAfterRecursiveWildcardPattern.test(spec)) {
return Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0;
}
}
/**
* Gets directories in a set of include patterns that should be watched for changes.
*/

Просмотреть файл

@ -8,7 +8,7 @@ namespace ts {
setWriter(writer: EmitTextWriter): void;
emitNodeWithComments(hint: EmitHint, node: Node, emitCallback: (hint: EmitHint, node: Node) => void): void;
emitBodyWithDetachedComments(node: Node, detachedRange: TextRange, emitCallback: (node: Node) => void): void;
emitTrailingCommentsOfPosition(pos: number): void;
emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean): void;
emitLeadingCommentsOfPosition(pos: number): void;
}
@ -306,7 +306,7 @@ namespace ts {
}
}
function emitTrailingCommentsOfPosition(pos: number) {
function emitTrailingCommentsOfPosition(pos: number, prefixSpace?: boolean) {
if (disabled) {
return;
}
@ -315,7 +315,7 @@ namespace ts {
performance.mark("beforeEmitTrailingCommentsOfPosition");
}
forEachTrailingCommentToEmit(pos, emitTrailingCommentOfPosition);
forEachTrailingCommentToEmit(pos, prefixSpace ? emitTrailingComment : emitTrailingCommentOfPosition);
if (extendedDiagnostics) {
performance.measure("commentTime", "beforeEmitTrailingCommentsOfPosition");
@ -415,17 +415,7 @@ namespace ts {
* @return true if the comment is a triple-slash comment else false
*/
function isTripleSlashComment(commentPos: number, commentEnd: number) {
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
// so that we don't end up computing comment string and doing match for all // comments
if (currentText.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
commentPos + 2 < commentEnd &&
currentText.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
const textSubStr = currentText.substring(commentPos, commentEnd);
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ?
true : false;
}
return false;
return isRecognizedTripleSlashComment(currentText, commentPos, commentEnd);
}
}
}

Просмотреть файл

@ -4,27 +4,13 @@
namespace ts {
// WARNING: The script `configureNightly.ts` uses a regexp to parse out these values.
// If changing the text in this section, be sure to test `configureNightly` too.
export const versionMajorMinor = "2.5";
export const versionMajorMinor = "2.6";
/** The version of the TypeScript compiler release */
export const version = `${versionMajorMinor}.0`;
}
/* @internal */
namespace ts {
/**
* Ternary values are defined such that
* x & y is False if either x or y is False.
* x & y is Maybe if either x or y is Maybe, but neither x or y is False.
* x & y is True if both x and y are True.
* x | y is False if both x and y are False.
* x | y is Maybe if either x or y is Maybe, but neither x or y is True.
* x | y is True if either x or y is True.
*/
export const enum Ternary {
False = 0,
Maybe = 1,
True = -1
}
// More efficient to create a collator once and use its `compare` than to call `a.localeCompare(b)` many times.
export const collator: { compare(a: string, b: string): number } = typeof Intl === "object" && typeof Intl.Collator === "function" ? new Intl.Collator(/*locales*/ undefined, { usage: "sort", sensitivity: "accent" }) : undefined;
@ -375,11 +361,11 @@ namespace ts {
return false;
}
export function filterMutate<T>(array: T[], f: (x: T) => boolean): void {
export function filterMutate<T>(array: T[], f: (x: T, i: number, array: T[]) => boolean): void {
let outIndex = 0;
for (const item of array) {
if (f(item)) {
array[outIndex] = item;
for (let i = 0; i < array.length; i++) {
if (f(array[i], i, array)) {
array[outIndex] = array[i];
outIndex++;
}
}
@ -515,13 +501,15 @@ namespace ts {
return result || array;
}
export function mapDefined<T, U>(array: ReadonlyArray<T>, mapFn: (x: T, i: number) => U | undefined): U[] {
export function mapDefined<T, U>(array: ReadonlyArray<T> | undefined, mapFn: (x: T, i: number) => U | undefined): U[] {
const result: U[] = [];
for (let i = 0; i < array.length; i++) {
const item = array[i];
const mapped = mapFn(item, i);
if (mapped !== undefined) {
result.push(mapped);
if (array) {
for (let i = 0; i < array.length; i++) {
const item = array[i];
const mapped = mapFn(item, i);
if (mapped !== undefined) {
result.push(mapped);
}
}
}
return result;
@ -711,7 +699,7 @@ namespace ts {
* are not present in `arrayA` but are present in `arrayB`. Assumes both arrays are sorted
* based on the provided comparer.
*/
export function relativeComplement<T>(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: (x: T, y: T) => Comparison = compareValues, offsetA = 0, offsetB = 0): T[] | undefined {
export function relativeComplement<T>(arrayA: T[] | undefined, arrayB: T[] | undefined, comparer: Comparer<T> = compareValues, offsetA = 0, offsetB = 0): T[] | undefined {
if (!arrayB || !arrayA || arrayB.length === 0 || arrayA.length === 0) return arrayB;
const result: T[] = [];
outer: for (; offsetB < arrayB.length; offsetB++) {
@ -1304,12 +1292,12 @@ namespace ts {
export function createFileDiagnostic(file: SourceFile, start: number, length: number, message: DiagnosticMessage): Diagnostic {
const end = start + length;
Debug.assert(start >= 0, "start must be non-negative, is " + start);
Debug.assert(length >= 0, "length must be non-negative, is " + length);
Debug.assertGreaterThanOrEqual(start, 0);
Debug.assertGreaterThanOrEqual(length, 0);
if (file) {
Debug.assert(start <= file.text.length, `start must be within the bounds of the file. ${start} > ${file.text.length}`);
Debug.assert(end <= file.text.length, `end must be the bounds of the file. ${end} > ${file.text.length}`);
Debug.assertLessThanOrEqual(start, file.text.length);
Debug.assertLessThanOrEqual(end, file.text.length);
}
let text = getLocaleSpecificMessage(message);
@ -1555,16 +1543,20 @@ namespace ts {
}
export function normalizePath(path: string): string {
return normalizePathAndParts(path).path;
}
export function normalizePathAndParts(path: string): { path: string, parts: string[] } {
path = normalizeSlashes(path);
const rootLength = getRootLength(path);
const root = path.substr(0, rootLength);
const normalized = getNormalizedParts(path, rootLength);
if (normalized.length) {
const joinedParts = root + normalized.join(directorySeparator);
return pathEndsWithDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts;
const parts = getNormalizedParts(path, rootLength);
if (parts.length) {
const joinedParts = root + parts.join(directorySeparator);
return { path: pathEndsWithDirectorySeparator(path) ? joinedParts + directorySeparator : joinedParts, parts };
}
else {
return root;
return { path: root, parts };
}
}
@ -1889,14 +1881,54 @@ namespace ts {
const reservedCharacterPattern = /[^\w\s\/]/g;
const wildcardCharCodes = [CharacterCodes.asterisk, CharacterCodes.question];
/**
* Matches any single directory segment unless it is the last segment and a .min.js file
* Breakdown:
* [^./] # matches everything up to the first . character (excluding directory seperators)
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
*/
const singleAsteriskRegexFragmentFiles = "([^./]|(\\.(?!min\\.js$))?)*";
const singleAsteriskRegexFragmentOther = "[^/]*";
/* @internal */
export const commonPackageFolders: ReadonlyArray<string> = ["node_modules", "bower_components", "jspm_packages"];
const implicitExcludePathRegexPattern = `(?!(${commonPackageFolders.join("|")})(/|$))`;
interface WildcardMatcher {
singleAsteriskRegexFragment: string;
doubleAsteriskRegexFragment: string;
replaceWildcardCharacter: (match: string) => string;
}
const filesMatcher: WildcardMatcher = {
/**
* Matches any single directory segment unless it is the last segment and a .min.js file
* Breakdown:
* [^./] # matches everything up to the first . character (excluding directory seperators)
* (\\.(?!min\\.js$))? # matches . characters but not if they are part of the .min.js file extension
*/
singleAsteriskRegexFragment: "([^./]|(\\.(?!min\\.js$))?)*",
/**
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
* files or directories, does not match subdirectories that start with a . character
*/
doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
replaceWildcardCharacter: match => replaceWildcardCharacter(match, filesMatcher.singleAsteriskRegexFragment)
};
const directoriesMatcher: WildcardMatcher = {
singleAsteriskRegexFragment: "[^/]*",
/**
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
* files or directories, does not match subdirectories that start with a . character
*/
doubleAsteriskRegexFragment: `(/${implicitExcludePathRegexPattern}[^/.][^/]*)*?`,
replaceWildcardCharacter: match => replaceWildcardCharacter(match, directoriesMatcher.singleAsteriskRegexFragment)
};
const excludeMatcher: WildcardMatcher = {
singleAsteriskRegexFragment: "[^/]*",
doubleAsteriskRegexFragment: "(/.+?)?",
replaceWildcardCharacter: match => replaceWildcardCharacter(match, excludeMatcher.singleAsteriskRegexFragment)
};
const wildcardMatchers = {
files: filesMatcher,
directories: directoriesMatcher,
exclude: excludeMatcher
};
export function getRegularExpressionForWildcard(specs: ReadonlyArray<string>, basePath: string, usage: "files" | "directories" | "exclude"): string | undefined {
const patterns = getRegularExpressionsForWildcards(specs, basePath, usage);
@ -1915,17 +1947,8 @@ namespace ts {
return undefined;
}
const replaceWildcardCharacter = usage === "files" ? replaceWildCardCharacterFiles : replaceWildCardCharacterOther;
const singleAsteriskRegexFragment = usage === "files" ? singleAsteriskRegexFragmentFiles : singleAsteriskRegexFragmentOther;
/**
* Regex for the ** wildcard. Matches any number of subdirectories. When used for including
* files or directories, does not match subdirectories that start with a . character
*/
const doubleAsteriskRegexFragment = usage === "exclude" ? "(/.+?)?" : "(/[^/.][^/]*)*?";
return flatMap(specs, spec =>
spec && getSubPatternFromSpec(spec, basePath, usage, singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter));
spec && getSubPatternFromSpec(spec, basePath, usage, wildcardMatchers[usage]));
}
/**
@ -1936,7 +1959,7 @@ namespace ts {
return !/[.*?]/.test(lastPathComponent);
}
function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", singleAsteriskRegexFragment: string, doubleAsteriskRegexFragment: string, replaceWildcardCharacter: (match: string) => string): string | undefined {
function getSubPatternFromSpec(spec: string, basePath: string, usage: "files" | "directories" | "exclude", { singleAsteriskRegexFragment, doubleAsteriskRegexFragment, replaceWildcardCharacter }: WildcardMatcher): string | undefined {
let subpattern = "";
let hasRecursiveDirectoryWildcard = false;
let hasWrittenComponent = false;
@ -1975,20 +1998,36 @@ namespace ts {
}
if (usage !== "exclude") {
let componentPattern = "";
// The * and ? wildcards should not match directories or files that start with . if they
// appear first in a component. Dotted directories and files can be included explicitly
// like so: **/.*/.*
if (component.charCodeAt(0) === CharacterCodes.asterisk) {
subpattern += "([^./]" + singleAsteriskRegexFragment + ")?";
componentPattern += "([^./]" + singleAsteriskRegexFragment + ")?";
component = component.substr(1);
}
else if (component.charCodeAt(0) === CharacterCodes.question) {
subpattern += "[^./]";
componentPattern += "[^./]";
component = component.substr(1);
}
}
subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
componentPattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
// Patterns should not include subfolders like node_modules unless they are
// explicitly included as part of the path.
//
// As an optimization, if the component pattern is the same as the component,
// then there definitely were no wildcard characters and we do not need to
// add the exclusion pattern.
if (componentPattern !== component) {
subpattern += implicitExcludePathRegexPattern;
}
subpattern += componentPattern;
}
else {
subpattern += component.replace(reservedCharacterPattern, replaceWildcardCharacter);
}
}
hasWrittenComponent = true;
@ -2002,14 +2041,6 @@ namespace ts {
return subpattern;
}
function replaceWildCardCharacterFiles(match: string) {
return replaceWildcardCharacter(match, singleAsteriskRegexFragmentFiles);
}
function replaceWildCardCharacterOther(match: string) {
return replaceWildcardCharacter(match, singleAsteriskRegexFragmentOther);
}
function replaceWildcardCharacter(match: string, singleAsteriskRegexFragment: string) {
return match === "*" ? singleAsteriskRegexFragment : match === "?" ? "[^/]" : "\\" + match;
}
@ -2183,20 +2214,14 @@ namespace ts {
/** Must have ".d.ts" first because if ".ts" goes first, that will be detected as the extension instead of ".d.ts". */
export const supportedTypescriptExtensionsForExtractExtension: ReadonlyArray<Extension> = [Extension.Dts, Extension.Ts, Extension.Tsx];
export const supportedJavascriptExtensions: ReadonlyArray<Extension> = [Extension.Js, Extension.Jsx];
const allSupportedExtensions = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions];
const allSupportedExtensions: ReadonlyArray<Extension> = [...supportedTypeScriptExtensions, ...supportedJavascriptExtensions];
export function getSupportedExtensions(options?: CompilerOptions, extraFileExtensions?: ReadonlyArray<JsFileExtensionInfo>): ReadonlyArray<string> {
const needAllExtensions = options && options.allowJs;
if (!extraFileExtensions || extraFileExtensions.length === 0 || !needAllExtensions) {
return needAllExtensions ? allSupportedExtensions : supportedTypeScriptExtensions;
}
const extensions: string[] = allSupportedExtensions.slice(0);
for (const extInfo of extraFileExtensions) {
if (extensions.indexOf(extInfo.extension) === -1) {
extensions.push(extInfo.extension);
}
}
return extensions;
return deduplicate([...allSupportedExtensions, ...extraFileExtensions.map(e => e.extension)]);
}
export function hasJavaScriptFileExtension(fileName: string) {
@ -2364,15 +2389,40 @@ namespace ts {
return currentAssertionLevel >= level;
}
export function assert(expression: boolean, message?: string, verboseDebugInfo?: () => string, stackCrawlMark?: Function): void {
export function assert(expression: boolean, message?: string, verboseDebugInfo?: string | (() => string), stackCrawlMark?: Function): void {
if (!expression) {
if (verboseDebugInfo) {
message += "\r\nVerbose Debug Information: " + verboseDebugInfo();
message += "\r\nVerbose Debug Information: " + (typeof verboseDebugInfo === "string" ? verboseDebugInfo : verboseDebugInfo());
}
fail(message ? "False expression: " + message : "False expression.", stackCrawlMark || assert);
}
}
export function assertEqual<T>(a: T, b: T, msg?: string, msg2?: string): void {
if (a !== b) {
const message = msg ? msg2 ? `${msg} ${msg2}` : msg : "";
fail(`Expected ${a} === ${b}. ${message}`);
}
}
export function assertLessThan(a: number, b: number, msg?: string): void {
if (a >= b) {
fail(`Expected ${a} < ${b}. ${msg || ""}`);
}
}
export function assertLessThanOrEqual(a: number, b: number): void {
if (a > b) {
fail(`Expected ${a} <= ${b}`);
}
}
export function assertGreaterThanOrEqual(a: number, b: number): void {
if (a < b) {
fail(`Expected ${a} >= ${b}`);
}
}
export function fail(message?: string, stackCrawlMark?: Function): void {
debugger;
const e = new Error(message ? `Debug Failure. ${message}` : "Debug Failure.");
@ -2538,6 +2588,11 @@ namespace ts {
}
Debug.fail(`File ${path} has unknown extension.`);
}
export function isAnySupportedFileExtension(path: string): boolean {
return tryGetExtensionFromPath(path) !== undefined;
}
export function tryGetExtensionFromPath(path: string): Extension | undefined {
return find<Extension>(supportedTypescriptExtensionsForExtractExtension, e => fileExtensionIs(path, e)) || find(supportedJavascriptExtensions, e => fileExtensionIs(path, e));
}

Просмотреть файл

@ -349,7 +349,6 @@ namespace ts {
errorNameNode = declaration.name;
const format = TypeFormatFlags.UseTypeOfFunction |
TypeFormatFlags.WriteClassExpressionAsTypeLiteral |
TypeFormatFlags.UseTypeAliasValue |
(shouldUseResolverType ? TypeFormatFlags.AddUndefined : 0);
resolver.writeTypeOfDeclaration(declaration, enclosingDeclaration, format, writer);
errorNameNode = undefined;
@ -368,7 +367,7 @@ namespace ts {
resolver.writeReturnTypeOfSignatureDeclaration(
signature,
enclosingDeclaration,
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue | TypeFormatFlags.WriteClassExpressionAsTypeLiteral,
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.WriteClassExpressionAsTypeLiteral,
writer);
errorNameNode = undefined;
}
@ -633,7 +632,7 @@ namespace ts {
resolver.writeTypeOfExpression(
expr,
enclosingDeclaration,
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.UseTypeAliasValue | TypeFormatFlags.WriteClassExpressionAsTypeLiteral,
TypeFormatFlags.UseTypeOfFunction | TypeFormatFlags.WriteClassExpressionAsTypeLiteral,
writer);
write(";");
writeLine();
@ -1367,6 +1366,10 @@ namespace ts {
}
function writeVariableStatement(node: VariableStatement) {
// If binding pattern doesn't have name, then there is nothing to be emitted for declaration file i.e. const [,] = [1,2].
if (every(node.declarationList && node.declarationList.declarations, decl => decl.name && isEmptyBindingPattern(decl.name))) {
return;
}
emitJsDocComments(node);
emitModuleElementDeclarationFlags(node);
if (isLet(node.declarationList)) {

Просмотреть файл

@ -1908,6 +1908,14 @@
"category": "Error",
"code": 2559
},
"Value of type '{0}' has no properties in common with type '{1}'. Did you mean to call it?": {
"category": "Error",
"code": 2560
},
"Base class expressions cannot reference class type parameters.": {
"category": "Error",
"code": 2561
},
"JSX element attributes type '{0}' may not be a union type.": {
"category": "Error",
"code": 2600
@ -2192,6 +2200,10 @@
"category": "Error",
"code": 2712
},
"Cannot access '{0}.{1}' because '{0}' is a type, but not a namespace. Did you mean to retrieve the type of the property '{1}' in '{0}' with '{0}[\"{1}\"]'?": {
"category": "Error",
"code": 2713
},
"Import declaration '{0}' is using private name '{1}'.": {
"category": "Error",
@ -2654,6 +2666,10 @@
"category": "Message",
"code": 6012
},
"Do not resolve the real path of symlinks.": {
"category": "Message",
"code": 6013
},
"Specify ECMAScript target version: 'ES3' (default), 'ES5', 'ES2015', 'ES2016', 'ES2017', or 'ESNEXT'.": {
"category": "Message",
"code": 6015
@ -3455,6 +3471,10 @@
"category": "Error",
"code": 8012
},
"'non-null assertions' can only be used in a .ts file.": {
"category": "Error",
"code": 8013
},
"'enum declarations' can only be used in a .ts file.": {
"category": "Error",
"code": 8015
@ -3653,6 +3673,10 @@
"category": "Message",
"code": 90025
},
"Rewrite as the indexed access type '{0}'.": {
"category": "Message",
"code": 90026
},
"Convert function to an ES2015 class": {
"category": "Message",
@ -3661,5 +3685,15 @@
"Convert function '{0}' to class": {
"category": "Message",
"code": 95002
},
"Extract function": {
"category": "Message",
"code": 95003
},
"Extract function into '{0}'": {
"category": "Message",
"code": 95004
}
}

Просмотреть файл

@ -1195,7 +1195,9 @@ namespace ts {
if (!(getEmitFlags(node) & EmitFlags.NoIndentation)) {
const dotRangeStart = node.expression.end;
const dotRangeEnd = skipTrivia(currentSourceFile.text, node.expression.end) + 1;
const dotToken = <Node>{ kind: SyntaxKind.DotToken, pos: dotRangeStart, end: dotRangeEnd };
const dotToken = createToken(SyntaxKind.DotToken);
dotToken.pos = dotRangeStart;
dotToken.end = dotRangeEnd;
indentBeforeDot = needsIndentation(node, node.expression, dotToken);
indentAfterDot = needsIndentation(node, dotToken, node.name);
}
@ -1346,7 +1348,9 @@ namespace ts {
emitExpression(node.left);
increaseIndentIf(indentBeforeOperator, isCommaOperator ? " " : undefined);
emitLeadingCommentsOfPosition(node.operatorToken.pos);
writeTokenNode(node.operatorToken);
emitTrailingCommentsOfPosition(node.operatorToken.end, /*prefixSpace*/ true); // Binary operators should have a space before the comment starts
increaseIndentIf(indentAfterOperator, " ");
emitExpression(node.right);
decreaseIndentIf(indentBeforeOperator, indentAfterOperator);
@ -1570,8 +1574,20 @@ namespace ts {
write(";");
}
function emitTokenWithComment(token: SyntaxKind, pos: number, contextNode?: Node) {
const node = contextNode && getParseTreeNode(contextNode);
if (node && node.kind === contextNode.kind) {
pos = skipTrivia(currentSourceFile.text, pos);
}
pos = writeToken(token, pos, /*contextNode*/ contextNode);
if (node && node.kind === contextNode.kind) {
emitTrailingCommentsOfPosition(pos, /*prefixSpace*/ true);
}
return pos;
}
function emitReturnStatement(node: ReturnStatement) {
writeToken(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node);
emitTokenWithComment(SyntaxKind.ReturnKeyword, node.pos, /*contextNode*/ node);
emitExpressionWithPrefix(" ", node.expression);
write(";");
}
@ -2131,10 +2147,12 @@ namespace ts {
function emitCatchClause(node: CatchClause) {
const openParenPos = writeToken(SyntaxKind.CatchKeyword, node.pos);
write(" ");
writeToken(SyntaxKind.OpenParenToken, openParenPos);
emit(node.variableDeclaration);
writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration ? node.variableDeclaration.end : openParenPos);
write(" ");
if (node.variableDeclaration) {
writeToken(SyntaxKind.OpenParenToken, openParenPos);
emit(node.variableDeclaration);
writeToken(SyntaxKind.CloseParenToken, node.variableDeclaration.end);
write(" ");
}
emit(node.block);
}

Просмотреть файл

@ -2128,14 +2128,14 @@ namespace ts {
: node;
}
export function createCatchClause(variableDeclaration: string | VariableDeclaration, block: Block) {
export function createCatchClause(variableDeclaration: string | VariableDeclaration | undefined, block: Block) {
const node = <CatchClause>createSynthesizedNode(SyntaxKind.CatchClause);
node.variableDeclaration = typeof variableDeclaration === "string" ? createVariableDeclaration(variableDeclaration) : variableDeclaration;
node.block = block;
return node;
}
export function updateCatchClause(node: CatchClause, variableDeclaration: VariableDeclaration, block: Block) {
export function updateCatchClause(node: CatchClause, variableDeclaration: VariableDeclaration | undefined, block: Block) {
return node.variableDeclaration !== variableDeclaration
|| node.block !== block
? updateNode(createCatchClause(variableDeclaration, block), node)
@ -2586,7 +2586,7 @@ namespace ts {
}
export function addSyntheticLeadingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
return setSyntheticLeadingComments(node, append(getSyntheticLeadingComments(node), <SynthesizedComment>{ kind, pos: -1, end: -1, hasTrailingNewLine, text }));
return setSyntheticLeadingComments(node, append<SynthesizedComment>(getSyntheticLeadingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
}
export function getSyntheticTrailingComments(node: Node): SynthesizedComment[] | undefined {
@ -2600,7 +2600,7 @@ namespace ts {
}
export function addSyntheticTrailingComment<T extends Node>(node: T, kind: SyntaxKind.SingleLineCommentTrivia | SyntaxKind.MultiLineCommentTrivia, text: string, hasTrailingNewLine?: boolean) {
return setSyntheticTrailingComments(node, append(getSyntheticTrailingComments(node), <SynthesizedComment>{ kind, pos: -1, end: -1, hasTrailingNewLine, text }));
return setSyntheticTrailingComments(node, append<SynthesizedComment>(getSyntheticTrailingComments(node), { kind, pos: -1, end: -1, hasTrailingNewLine, text }));
}
/**

Просмотреть файл

@ -13,13 +13,32 @@ namespace ts {
return compilerOptions.traceResolution && host.trace !== undefined;
}
/**
* Result of trying to resolve a module.
* At least one of `ts` and `js` should be defined, or the whole thing should be `undefined`.
*/
/** Array that is only intended to be pushed to, never read. */
/* @internal */
export interface Push<T> {
push(value: T): void;
}
function withPackageId(packageId: PackageId | undefined, r: PathAndExtension | undefined): Resolved {
return r && { path: r.path, extension: r.ext, packageId };
}
function noPackageId(r: PathAndExtension | undefined): Resolved {
return withPackageId(/*packageId*/ undefined, r);
}
/** Result of trying to resolve a module. */
interface Resolved {
path: string;
extension: Extension;
packageId: PackageId | undefined;
}
/** Result of trying to resolve a module at a file. Needs to have 'packageId' added later. */
interface PathAndExtension {
path: string;
// (Use a different name than `extension` to make sure Resolved isn't assignable to PathAndExtension.)
ext: Extension;
}
/**
@ -43,7 +62,7 @@ namespace ts {
function createResolvedModuleWithFailedLookupLocations(resolved: Resolved | undefined, isExternalLibraryImport: boolean, failedLookupLocations: string[]): ResolvedModuleWithFailedLookupLocations {
return {
resolvedModule: resolved && { resolvedFileName: resolved.path, extension: resolved.extension, isExternalLibraryImport },
resolvedModule: resolved && { resolvedFileName: resolved.path, extension: resolved.extension, isExternalLibraryImport, packageId: resolved.packageId },
failedLookupLocations
};
}
@ -54,9 +73,16 @@ namespace ts {
traceEnabled: boolean;
}
interface PackageJson {
name?: string;
version?: string;
typings?: string;
types?: string;
main?: string;
}
/** Reads from "main" or "types"/"typings" depending on `extensions`. */
function tryReadPackageJsonFields(readTypes: boolean, packageJsonPath: string, baseDirectory: string, state: ModuleResolutionState): string | undefined {
const jsonContent = readJson(packageJsonPath, state.host);
function tryReadPackageJsonFields(readTypes: boolean, jsonContent: PackageJson, baseDirectory: string, state: ModuleResolutionState): string | undefined {
return readTypes ? tryReadFromField("typings") || tryReadFromField("types") : tryReadFromField("main");
function tryReadFromField(fieldName: "typings" | "types" | "main"): string | undefined {
@ -83,7 +109,7 @@ namespace ts {
}
}
function readJson(path: string, host: ModuleResolutionHost): { typings?: string, types?: string, main?: string } {
function readJson(path: string, host: ModuleResolutionHost): PackageJson {
try {
const jsonText = host.readFile(path);
return jsonText ? JSON.parse(jsonText) : {};
@ -174,7 +200,10 @@ namespace ts {
let resolvedTypeReferenceDirective: ResolvedTypeReferenceDirective | undefined;
if (resolved) {
resolved = realpath(resolved, host, traceEnabled);
if (!options.preserveSymlinks) {
resolved = realPath(resolved, host, traceEnabled);
}
if (traceEnabled) {
trace(host, Diagnostics.Type_reference_directive_0_was_successfully_resolved_to_1_primary_Colon_2, typeReferenceDirectiveName, resolved, primary);
}
@ -646,7 +675,7 @@ namespace ts {
if (extension !== undefined) {
const path = tryFile(candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state);
if (path !== undefined) {
return { path, extension };
return { path, extension, packageId: undefined };
}
}
@ -678,7 +707,7 @@ namespace ts {
const { resolvedModule, failedLookupLocations } =
nodeModuleNameResolverWorker(moduleName, initialDir, { moduleResolution: ts.ModuleResolutionKind.NodeJs, allowJs: true }, host, /*cache*/ undefined, /*jsOnly*/ true);
if (!resolvedModule) {
throw new Error(`Could not resolve JS module ${moduleName} starting at ${initialDir}. Looked in: ${failedLookupLocations.join(", ")}`);
throw new Error(`Could not resolve JS module '${moduleName}' starting at '${initialDir}'. Looked in: ${failedLookupLocations.join(", ")}`);
}
return resolvedModule.resolvedFileName;
}
@ -708,18 +737,25 @@ namespace ts {
trace(host, Diagnostics.Loading_module_0_from_node_modules_folder_target_file_type_1, moduleName, Extensions[extensions]);
}
const resolved = loadModuleFromNodeModules(extensions, moduleName, containingDirectory, failedLookupLocations, state, cache);
if (!resolved) return undefined;
let resolvedValue = resolved.value;
if (!compilerOptions.preserveSymlinks) {
resolvedValue = resolvedValue && { ...resolved.value, path: realPath(resolved.value.path, host, traceEnabled), extension: resolved.value.extension };
}
// For node_modules lookups, get the real path so that multiple accesses to an `npm link`-ed module do not create duplicate files.
return resolved && { value: resolved.value && { resolved: { path: realpath(resolved.value.path, host, traceEnabled), extension: resolved.value.extension }, isExternalLibraryImport: true } };
return { value: resolvedValue && { resolved: resolvedValue, isExternalLibraryImport: true } };
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
const { path: candidate, parts } = normalizePathAndParts(combinePaths(containingDirectory, moduleName));
const resolved = nodeLoadModuleByRelativeName(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state, /*considerPackageJson*/ true);
return resolved && toSearchResult({ resolved, isExternalLibraryImport: false });
// Treat explicit "node_modules" import as an external library import.
return resolved && toSearchResult({ resolved, isExternalLibraryImport: contains(parts, "node_modules") });
}
}
}
function realpath(path: string, host: ModuleResolutionHost, traceEnabled: boolean): string {
function realPath(path: string, host: ModuleResolutionHost, traceEnabled: boolean): string {
if (!host.realpath) {
return path;
}
@ -747,7 +783,7 @@ namespace ts {
}
const resolvedFromFile = loadModuleFromFile(extensions, candidate, failedLookupLocations, onlyRecordFailures, state);
if (resolvedFromFile) {
return resolvedFromFile;
return noPackageId(resolvedFromFile);
}
}
if (!onlyRecordFailures) {
@ -768,11 +804,15 @@ namespace ts {
return !host.directoryExists || host.directoryExists(directoryName);
}
function loadModuleFromFileNoPackageId(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved {
return noPackageId(loadModuleFromFile(extensions, candidate, failedLookupLocations, onlyRecordFailures, state));
}
/**
* @param {boolean} onlyRecordFailures - if true then function won't try to actually load files but instead record all attempts as failures. This flag is necessary
* in cases when we know upfront that all load attempts will fail (because containing folder does not exists) however we still need to record all failed lookup locations.
*/
function loadModuleFromFile(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
function loadModuleFromFile(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
// First, try adding an extension. An import of "foo" could be matched by a file "foo.ts", or "foo.js" by "foo.js.ts"
const resolvedByAddingExtension = tryAddingExtensions(candidate, extensions, failedLookupLocations, onlyRecordFailures, state);
if (resolvedByAddingExtension) {
@ -792,7 +832,7 @@ namespace ts {
}
/** Try to return an existing file that adds one of the `extensions` to `candidate`. */
function tryAddingExtensions(candidate: string, extensions: Extensions, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): Resolved | undefined {
function tryAddingExtensions(candidate: string, extensions: Extensions, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState): PathAndExtension | undefined {
if (!onlyRecordFailures) {
// check if containing folder exists - if it doesn't then just record failures for all supported extensions without disk probing
const directory = getDirectoryPath(candidate);
@ -810,9 +850,9 @@ namespace ts {
return tryExtension(Extension.Js) || tryExtension(Extension.Jsx);
}
function tryExtension(extension: Extension): Resolved | undefined {
const path = tryFile(candidate + extension, failedLookupLocations, onlyRecordFailures, state);
return path && { path, extension };
function tryExtension(ext: Extension): PathAndExtension | undefined {
const path = tryFile(candidate + ext, failedLookupLocations, onlyRecordFailures, state);
return path && { path, ext };
}
}
@ -838,12 +878,23 @@ namespace ts {
function loadNodeModuleFromDirectory(extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, onlyRecordFailures: boolean, state: ModuleResolutionState, considerPackageJson = true): Resolved | undefined {
const directoryExists = !onlyRecordFailures && directoryProbablyExists(candidate, state.host);
let packageId: PackageId | undefined;
if (considerPackageJson) {
const packageJsonPath = pathToPackageJson(candidate);
if (directoryExists && state.host.fileExists(packageJsonPath)) {
const fromPackageJson = loadModuleFromPackageJson(packageJsonPath, extensions, candidate, failedLookupLocations, state);
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const jsonContent = readJson(packageJsonPath, state.host);
if (typeof jsonContent.name === "string" && typeof jsonContent.version === "string") {
packageId = { name: jsonContent.name, version: jsonContent.version };
}
const fromPackageJson = loadModuleFromPackageJson(jsonContent, extensions, candidate, failedLookupLocations, state);
if (fromPackageJson) {
return fromPackageJson;
return withPackageId(packageId, fromPackageJson);
}
}
else {
@ -855,15 +906,11 @@ namespace ts {
}
}
return loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state);
return withPackageId(packageId, loadModuleFromFile(extensions, combinePaths(candidate, "index"), failedLookupLocations, !directoryExists, state));
}
function loadModuleFromPackageJson(packageJsonPath: string, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
if (state.traceEnabled) {
trace(state.host, Diagnostics.Found_package_json_at_0, packageJsonPath);
}
const file = tryReadPackageJsonFields(extensions !== Extensions.JavaScript, packageJsonPath, candidate, state);
function loadModuleFromPackageJson(jsonContent: PackageJson, extensions: Extensions, candidate: string, failedLookupLocations: Push<string>, state: ModuleResolutionState): PathAndExtension | undefined {
const file = tryReadPackageJsonFields(extensions !== Extensions.JavaScript, jsonContent, candidate, state);
if (!file) {
return undefined;
}
@ -883,13 +930,18 @@ namespace ts {
// Even if extensions is DtsOnly, we can still look up a .ts file as a result of package.json "types"
const nextExtensions = extensions === Extensions.DtsOnly ? Extensions.TypeScript : extensions;
// Don't do package.json lookup recursively, because Node.js' package lookup doesn't.
return nodeLoadModuleByRelativeName(nextExtensions, file, failedLookupLocations, onlyRecordFailures, state, /*considerPackageJson*/ false);
const result = nodeLoadModuleByRelativeName(nextExtensions, file, failedLookupLocations, onlyRecordFailures, state, /*considerPackageJson*/ false);
if (result) {
// It won't have a `packageId` set, because we disabled `considerPackageJson`.
Debug.assert(result.packageId === undefined);
return { path: result.path, ext: result.extension };
}
}
/** Resolve from an arbitrarily specified file. Return `undefined` if it has an unsupported extension. */
function resolvedIfExtensionMatches(extensions: Extensions, path: string): Resolved | undefined {
const extension = tryGetExtensionFromPath(path);
return extension !== undefined && extensionIsOk(extensions, extension) ? { path, extension } : undefined;
function resolvedIfExtensionMatches(extensions: Extensions, path: string): PathAndExtension | undefined {
const ext = tryGetExtensionFromPath(path);
return ext !== undefined && extensionIsOk(extensions, ext) ? { path, ext } : undefined;
}
/** True if `extension` is one of the supported `extensions`. */
@ -911,7 +963,7 @@ namespace ts {
function loadModuleFromNodeModulesFolder(extensions: Extensions, moduleName: string, nodeModulesFolder: string, nodeModulesFolderExists: boolean, failedLookupLocations: Push<string>, state: ModuleResolutionState): Resolved | undefined {
const candidate = normalizePath(combinePaths(nodeModulesFolder, moduleName));
return loadModuleFromFile(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
return loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state) ||
loadNodeModuleFromDirectory(extensions, candidate, failedLookupLocations, !nodeModulesFolderExists, state);
}
@ -996,7 +1048,7 @@ namespace ts {
if (traceEnabled) {
trace(host, Diagnostics.Resolution_for_module_0_was_found_in_cache, moduleName);
}
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension } };
return { value: result.resolvedModule && { path: result.resolvedModule.resolvedFileName, extension: result.resolvedModule.extension, packageId: result.resolvedModule.packageId } };
}
}
@ -1010,7 +1062,7 @@ namespace ts {
return createResolvedModuleWithFailedLookupLocations(resolved && resolved.value, /*isExternalLibraryImport*/ false, failedLookupLocations);
function tryResolve(extensions: Extensions): SearchResult<Resolved> {
const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFile, failedLookupLocations, state);
const resolvedUsingSettings = tryLoadModuleUsingOptionalResolutionSettings(extensions, moduleName, containingDirectory, loadModuleFromFileNoPackageId, failedLookupLocations, state);
if (resolvedUsingSettings) {
return { value: resolvedUsingSettings };
}
@ -1024,7 +1076,7 @@ namespace ts {
return resolutionFromCache;
}
const searchName = normalizePath(combinePaths(directory, moduleName));
return toSearchResult(loadModuleFromFile(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state));
return toSearchResult(loadModuleFromFileNoPackageId(extensions, searchName, failedLookupLocations, /*onlyRecordFailures*/ false, state));
});
if (resolved) {
return resolved;
@ -1036,7 +1088,7 @@ namespace ts {
}
else {
const candidate = normalizePath(combinePaths(containingDirectory, moduleName));
return toSearchResult(loadModuleFromFile(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state));
return toSearchResult(loadModuleFromFileNoPackageId(extensions, candidate, failedLookupLocations, /*onlyRecordFailures*/ false, state));
}
}
}

Просмотреть файл

@ -2061,7 +2061,7 @@ namespace ts {
return <TemplateMiddle | TemplateTail>fragment;
}
function parseLiteralLikeNode(kind: SyntaxKind): LiteralLikeNode {
function parseLiteralLikeNode(kind: SyntaxKind): LiteralExpression | LiteralLikeNode {
const node = <LiteralExpression>createNode(kind);
const text = scanner.getTokenValue();
node.text = text;
@ -2611,11 +2611,31 @@ namespace ts {
return token() === SyntaxKind.DotToken ? undefined : node;
}
function parseLiteralTypeNode(): LiteralTypeNode {
const node = <LiteralTypeNode>createNode(SyntaxKind.LiteralType);
node.literal = parseSimpleUnaryExpression();
finishNode(node);
return node;
function parseLiteralTypeNode(negative?: boolean): LiteralTypeNode {
const node = createNode(SyntaxKind.LiteralType) as LiteralTypeNode;
let unaryMinusExpression: PrefixUnaryExpression;
if (negative) {
unaryMinusExpression = createNode(SyntaxKind.PrefixUnaryExpression) as PrefixUnaryExpression;
unaryMinusExpression.operator = SyntaxKind.MinusToken;
nextToken();
}
let expression: UnaryExpression;
switch (token()) {
case SyntaxKind.StringLiteral:
case SyntaxKind.NumericLiteral:
expression = parseLiteralLikeNode(token()) as LiteralExpression;
break;
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
expression = parseTokenNode();
}
if (negative) {
unaryMinusExpression.operand = expression;
finishNode(unaryMinusExpression);
expression = unaryMinusExpression;
}
node.literal = expression;
return finishNode(node);
}
function nextTokenIsNumericLiteral() {
@ -2650,7 +2670,7 @@ namespace ts {
case SyntaxKind.FalseKeyword:
return parseLiteralTypeNode();
case SyntaxKind.MinusToken:
return lookAhead(nextTokenIsNumericLiteral) ? parseLiteralTypeNode() : parseTypeReference();
return lookAhead(nextTokenIsNumericLiteral) ? parseLiteralTypeNode(/*negative*/ true) : parseTypeReference();
case SyntaxKind.VoidKeyword:
case SyntaxKind.NullKeyword:
return parseTokenNode<TypeNode>();
@ -2700,6 +2720,7 @@ namespace ts {
case SyntaxKind.TrueKeyword:
case SyntaxKind.FalseKeyword:
case SyntaxKind.ObjectKeyword:
case SyntaxKind.AsteriskToken:
return true;
case SyntaxKind.MinusToken:
return lookAhead(nextTokenIsNumericLiteral);
@ -3181,7 +3202,7 @@ namespace ts {
return undefined;
}
const isAsync = !!(getModifierFlags(arrowFunction) & ModifierFlags.Async);
const isAsync = hasModifier(arrowFunction, ModifierFlags.Async);
// If we have an arrow, then try to parse the body. Even if not, try to parse if we
// have an opening brace, just in case we're in an error state.
@ -3361,7 +3382,7 @@ namespace ts {
function parseParenthesizedArrowFunctionExpressionHead(allowAmbiguity: boolean): ArrowFunction {
const node = <ArrowFunction>createNode(SyntaxKind.ArrowFunction);
node.modifiers = parseModifiersForArrowFunction();
const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
// Arrow functions are never generators.
//
@ -4494,7 +4515,7 @@ namespace ts {
node.asteriskToken = parseOptionalToken(SyntaxKind.AsteriskToken);
const isGenerator = node.asteriskToken ? SignatureFlags.Yield : SignatureFlags.None;
const isAsync = (getModifierFlags(node) & ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
const isAsync = hasModifier(node, ModifierFlags.Async) ? SignatureFlags.Await : SignatureFlags.None;
node.name =
isGenerator && isAsync ? doInYieldAndAwaitContext(parseOptionalIdentifier) :
isGenerator ? doInYieldContext(parseOptionalIdentifier) :
@ -4778,11 +4799,16 @@ namespace ts {
function parseCatchClause(): CatchClause {
const result = <CatchClause>createNode(SyntaxKind.CatchClause);
parseExpected(SyntaxKind.CatchKeyword);
if (parseExpected(SyntaxKind.OpenParenToken)) {
if (parseOptional(SyntaxKind.OpenParenToken)) {
result.variableDeclaration = parseVariableDeclaration();
parseExpected(SyntaxKind.CloseParenToken);
}
else {
// Keep shape of node to avoid degrading performance.
result.variableDeclaration = undefined;
}
parseExpected(SyntaxKind.CloseParenToken);
result.block = parseBlock(/*ignoreMissingOpenBrace*/ false);
return finishNode(result);
}
@ -6143,7 +6169,7 @@ namespace ts {
export function parseIsolatedJSDocComment(content: string, start: number, length: number): { jsDoc: JSDoc, diagnostics: Diagnostic[] } | undefined {
initializeState(content, ScriptTarget.Latest, /*_syntaxCursor:*/ undefined, ScriptKind.JS);
sourceFile = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content };
sourceFile = <SourceFile>{ languageVariant: LanguageVariant.Standard, text: content }; // tslint:disable-line no-object-literal-type-assertion
const jsDoc = parseJSDocCommentWorker(start, length);
const diagnostics = parseDiagnostics;
clearState();

Просмотреть файл

@ -472,6 +472,15 @@ namespace ts {
resolveTypeReferenceDirectiveNamesWorker = (typeReferenceDirectiveNames, containingFile) => loadWithLocalCache(checkAllDefined(typeReferenceDirectiveNames), containingFile, loader);
}
// Map from a stringified PackageId to the source file with that id.
// Only one source file may have a given packageId. Others become redirects (see createRedirectSourceFile).
// `packageIdToSourceFile` is only used while building the program, while `sourceFileToPackageName` and `isSourceFileTargetOfRedirect` are kept around.
const packageIdToSourceFile = createMap<SourceFile>();
// Maps from a SourceFile's `.path` to the name of the package it was imported with.
let sourceFileToPackageName = createMap<string>();
// See `sourceFileIsRedirectedTo`.
let redirectTargetsSet = createMap<true>();
const filesByName = createMap<SourceFile | undefined>();
// stores 'filename -> file association' ignoring case
// used to track cases when two file names differ only in casing
@ -548,6 +557,8 @@ namespace ts {
isSourceFileFromExternalLibrary,
dropDiagnosticsProducingTypeChecker,
getSourceFileFromReference,
sourceFileToPackageName,
redirectTargetsSet,
};
verifyCompilerOptions();
@ -773,8 +784,12 @@ namespace ts {
const modifiedSourceFiles: { oldFile: SourceFile, newFile: SourceFile }[] = [];
oldProgram.structureIsReused = StructureIsReused.Completely;
for (const oldSourceFile of oldProgram.getSourceFiles()) {
const newSourceFile = host.getSourceFileByPath
const oldSourceFiles = oldProgram.getSourceFiles();
const enum SeenPackageName { Exists, Modified }
const seenPackageNames = createMap<SeenPackageName>();
for (const oldSourceFile of oldSourceFiles) {
let newSourceFile = host.getSourceFileByPath
? host.getSourceFileByPath(oldSourceFile.fileName, oldSourceFile.path, options.target)
: host.getSourceFile(oldSourceFile.fileName, options.target);
@ -782,10 +797,46 @@ namespace ts {
return oldProgram.structureIsReused = StructureIsReused.Not;
}
Debug.assert(!newSourceFile.redirectInfo, "Host should not return a redirect source file from `getSourceFile`");
let fileChanged: boolean;
if (oldSourceFile.redirectInfo) {
// We got `newSourceFile` by path, so it is actually for the unredirected file.
// This lets us know if the unredirected file has changed. If it has we should break the redirect.
if (newSourceFile !== oldSourceFile.redirectInfo.unredirected) {
// Underlying file has changed. Might not redirect anymore. Must rebuild program.
return oldProgram.structureIsReused = StructureIsReused.Not;
}
fileChanged = false;
newSourceFile = oldSourceFile; // Use the redirect.
}
else if (oldProgram.redirectTargetsSet.has(oldSourceFile.path)) {
// If a redirected-to source file changes, the redirect may be broken.
if (newSourceFile !== oldSourceFile) {
return oldProgram.structureIsReused = StructureIsReused.Not;
}
fileChanged = false;
}
else {
fileChanged = newSourceFile !== oldSourceFile;
}
newSourceFile.path = oldSourceFile.path;
filePaths.push(newSourceFile.path);
if (oldSourceFile !== newSourceFile) {
const packageName = oldProgram.sourceFileToPackageName.get(oldSourceFile.path);
if (packageName !== undefined) {
// If there are 2 different source files for the same package name and at least one of them changes,
// they might become redirects. So we must rebuild the program.
const prevKind = seenPackageNames.get(packageName);
const newKind = fileChanged ? SeenPackageName.Modified : SeenPackageName.Exists;
if ((prevKind !== undefined && newKind === SeenPackageName.Modified) || prevKind === SeenPackageName.Modified) {
return oldProgram.structureIsReused = StructureIsReused.Not;
}
seenPackageNames.set(packageName, newKind);
}
if (fileChanged) {
// The `newSourceFile` object was created for the new program.
if (oldSourceFile.hasNoDefaultLib !== newSourceFile.hasNoDefaultLib) {
@ -897,6 +948,9 @@ namespace ts {
}
resolvedTypeReferenceDirectives = oldProgram.getResolvedTypeReferenceDirectives();
sourceFileToPackageName = oldProgram.sourceFileToPackageName;
redirectTargetsSet = oldProgram.redirectTargetsSet;
return oldProgram.structureIsReused = StructureIsReused.Completely;
}
@ -1196,10 +1250,14 @@ namespace ts {
case SyntaxKind.EnumDeclaration:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.enum_declarations_can_only_be_used_in_a_ts_file));
return;
case SyntaxKind.TypeAssertionExpression:
const typeAssertionExpression = <TypeAssertion>node;
diagnostics.push(createDiagnosticForNode(typeAssertionExpression.type, Diagnostics.type_assertion_expressions_can_only_be_used_in_a_ts_file));
case SyntaxKind.NonNullExpression:
diagnostics.push(createDiagnosticForNode(node, Diagnostics.non_null_assertions_can_only_be_used_in_a_ts_file));
return;
case SyntaxKind.AsExpression:
diagnostics.push(createDiagnosticForNode((node as AsExpression).type, Diagnostics.type_assertion_expressions_can_only_be_used_in_a_ts_file));
return;
case SyntaxKind.TypeAssertionExpression:
Debug.fail(); // Won't parse these in a JS file anyway, as they are interpreted as JSX.
}
const prevParent = parent;
@ -1533,7 +1591,7 @@ namespace ts {
/** This has side effects through `findSourceFile`. */
function processSourceFile(fileName: string, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): void {
getSourceFileFromReferenceWorker(fileName,
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd),
fileName => findSourceFile(fileName, toPath(fileName), isDefaultLib, refFile, refPos, refEnd, /*packageId*/ undefined),
(diagnostic, ...args) => {
fileProcessingDiagnostics.add(refFile !== undefined && refEnd !== undefined && refPos !== undefined
? createFileDiagnostic(refFile, refPos, refEnd - refPos, diagnostic, ...args)
@ -1552,8 +1610,26 @@ namespace ts {
}
}
function createRedirectSourceFile(redirectTarget: SourceFile, unredirected: SourceFile, fileName: string, path: Path): SourceFile {
const redirect: SourceFile = Object.create(redirectTarget);
redirect.fileName = fileName;
redirect.path = path;
redirect.redirectInfo = { redirectTarget, unredirected };
Object.defineProperties(redirect, {
id: {
get(this: SourceFile) { return this.redirectInfo.redirectTarget.id; },
set(this: SourceFile, value: SourceFile["id"]) { this.redirectInfo.redirectTarget.id = value; },
},
symbol: {
get(this: SourceFile) { return this.redirectInfo.redirectTarget.symbol; },
set(this: SourceFile, value: SourceFile["symbol"]) { this.redirectInfo.redirectTarget.symbol = value; },
},
});
return redirect;
}
// Get source file from normalized fileName
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile?: SourceFile, refPos?: number, refEnd?: number): SourceFile {
function findSourceFile(fileName: string, path: Path, isDefaultLib: boolean, refFile: SourceFile, refPos: number, refEnd: number, packageId: PackageId | undefined): SourceFile | undefined {
if (filesByName.has(path)) {
const file = filesByName.get(path);
// try to check if we've already seen this file but with a different casing in path
@ -1596,6 +1672,26 @@ namespace ts {
}
});
if (packageId) {
const packageIdKey = `${packageId.name}@${packageId.version}`;
const fileFromPackageId = packageIdToSourceFile.get(packageIdKey);
if (fileFromPackageId) {
// Some other SourceFile already exists with this package name and version.
// Instead of creating a duplicate, just redirect to the existing one.
const dupFile = createRedirectSourceFile(fileFromPackageId, file, fileName, path);
redirectTargetsSet.set(fileFromPackageId.path, true);
filesByName.set(path, dupFile);
sourceFileToPackageName.set(path, packageId.name);
files.push(dupFile);
return dupFile;
}
else if (file) {
// This is the first source file to have this packageId.
packageIdToSourceFile.set(packageIdKey, file);
sourceFileToPackageName.set(path, packageId.name);
}
}
filesByName.set(path, file);
if (file) {
sourceFilesFoundSearchingNodeModules.set(path, currentNodeModulesDepth > 0);
@ -1758,7 +1854,7 @@ namespace ts {
else if (shouldAddFile) {
const path = toPath(resolvedFileName);
const pos = skipTrivia(file.text, file.imports[i].pos);
findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, file, pos, file.imports[i].end);
findSourceFile(resolvedFileName, path, /*isDefaultLib*/ false, file, pos, file.imports[i].end, resolution.packageId);
}
if (isFromNodeModulesSearch) {

Просмотреть файл

@ -4,6 +4,18 @@ declare function setTimeout(handler: (...args: any[]) => void, timeout: number):
declare function clearTimeout(handle: any): void;
namespace ts {
/**
* Set a high stack trace limit to provide more information in case of an error.
* Called for command-line and server use cases.
* Not called if TypeScript is used as a library.
*/
/* @internal */
export function setStackTraceLimit() {
if ((Error as any).stackTraceLimit < 100) { // Also tests that we won't set the property if it doesn't exist.
(Error as any).stackTraceLimit = 100;
}
}
export enum FileWatcherEventKind {
Created,
Changed,
@ -141,7 +153,7 @@ namespace ts {
return;
}
watcher = _fs.watch(
dirPath,
dirPath || ".",
{ persistent: true },
(eventName: string, relativeFileName: string) => fileEventHandler(eventName, relativeFileName, dirPath)
);

Просмотреть файл

@ -331,11 +331,14 @@ namespace ts {
location
);
}
else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0)) {
else if (numElements !== 1 && (flattenContext.level < FlattenLevel.ObjectRest || numElements === 0)
|| every(elements, isOmittedExpression)) {
// For anything other than a single-element destructuring we need to generate a temporary
// to ensure value is evaluated exactly once. Additionally, if we have zero elements
// we need to emit *something* to ensure that in case a 'var' keyword was already emitted,
// so in that case, we'll intentionally create that temporary.
// Or all the elements of the binding pattern are omitted expression such as "var [,] = [1,2]",
// then we will create temporary variable.
const reuseIdentifierExpressions = !isDeclarationBindingElement(parent) || numElements !== 0;
value = ensureIdentifier(flattenContext, value, reuseIdentifierExpressions, location);
}

Просмотреть файл

@ -394,7 +394,7 @@ namespace ts {
function shouldVisitNode(node: Node): boolean {
return (node.transformFlags & TransformFlags.ContainsES2015) !== 0
|| convertedLoopState !== undefined
|| (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && isStatement(node))
|| (hierarchyFacts & HierarchyFacts.ConstructorWithCapturedSuper && (isStatement(node) || (node.kind === SyntaxKind.Block)))
|| (isIterationStatement(node, /*lookInLabeledStatements*/ false) && shouldConvertIterationStatementBody(node))
|| isTypeScriptClassWrapper(node);
}
@ -840,7 +840,7 @@ namespace ts {
outer.end = skipTrivia(currentText, node.pos);
setEmitFlags(outer, EmitFlags.NoComments);
return createParen(
const result = createParen(
createCall(
outer,
/*typeArguments*/ undefined,
@ -849,6 +849,8 @@ namespace ts {
: []
)
);
addSyntheticLeadingComment(result, SyntaxKind.MultiLineCommentTrivia, "* @class ");
return result;
}
/**
@ -2105,13 +2107,14 @@ namespace ts {
setCommentRange(declarationList, node);
if (node.transformFlags & TransformFlags.ContainsBindingPattern
&& (isBindingPattern(node.declarations[0].name)
|| isBindingPattern(lastOrUndefined(node.declarations).name))) {
&& (isBindingPattern(node.declarations[0].name) || isBindingPattern(lastOrUndefined(node.declarations).name))) {
// If the first or last declaration is a binding pattern, we need to modify
// the source map range for the declaration list.
const firstDeclaration = firstOrUndefined(declarations);
const lastDeclaration = lastOrUndefined(declarations);
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
if (firstDeclaration) {
const lastDeclaration = lastOrUndefined(declarations);
setSourceMapRange(declarationList, createRange(firstDeclaration.pos, lastDeclaration.end));
}
}
return declarationList;
@ -2491,7 +2494,7 @@ namespace ts {
const catchVariable = getGeneratedNameForNode(errorRecord);
const returnMethod = createTempVariable(/*recordTempVariable*/ undefined);
const values = createValuesHelper(context, expression, node.expression);
const next = createCall(createPropertyAccess(iterator, "next" ), /*typeArguments*/ undefined, []);
const next = createCall(createPropertyAccess(iterator, "next"), /*typeArguments*/ undefined, []);
hoistVariableDeclaration(errorRecord);
hoistVariableDeclaration(returnMethod);
@ -3173,6 +3176,7 @@ namespace ts {
function visitCatchClause(node: CatchClause): CatchClause {
const ancestorFacts = enterSubtree(HierarchyFacts.BlockScopeExcludes, HierarchyFacts.BlockScopeIncludes);
let updated: CatchClause;
Debug.assert(!!node.variableDeclaration, "Catch clause variable should always be present when downleveling ES2015.");
if (isBindingPattern(node.variableDeclaration.name)) {
const temp = createTempVariable(/*recordTempVariable*/ undefined);
const newVariableDeclaration = createVariableDeclaration(temp);

Просмотреть файл

@ -101,6 +101,8 @@ namespace ts {
return visitExpressionStatement(node as ExpressionStatement);
case SyntaxKind.ParenthesizedExpression:
return visitParenthesizedExpression(node as ParenthesizedExpression, noDestructuringValue);
case SyntaxKind.CatchClause:
return visitCatchClause(node as CatchClause);
default:
return visitEachChild(node, visitor, context);
}
@ -212,6 +214,17 @@ namespace ts {
return visitEachChild(node, noDestructuringValue ? visitorNoDestructuringValue : visitor, context);
}
function visitCatchClause(node: CatchClause): CatchClause {
if (!node.variableDeclaration) {
return updateCatchClause(
node,
createVariableDeclaration(createTempVariable(/*recordTempVariable*/ undefined)),
visitNode(node.block, visitor, isBlock)
);
}
return visitEachChild(node, visitor, context);
}
/**
* Visits a BinaryExpression that contains a destructuring assignment.
*

Просмотреть файл

@ -164,12 +164,11 @@ namespace ts {
}
// A generated code block
interface CodeBlock {
kind: CodeBlockKind;
}
type CodeBlock = | ExceptionBlock | LabeledBlock | SwitchBlock | LoopBlock | WithBlock;
// a generated exception block, used for 'try' statements
interface ExceptionBlock extends CodeBlock {
interface ExceptionBlock {
kind: CodeBlockKind.Exception;
state: ExceptionBlockState;
startLabel: Label;
catchVariable?: Identifier;
@ -179,27 +178,31 @@ namespace ts {
}
// A generated code that tracks the target for 'break' statements in a LabeledStatement.
interface LabeledBlock extends CodeBlock {
interface LabeledBlock {
kind: CodeBlockKind.Labeled;
labelText: string;
isScript: boolean;
breakLabel: Label;
}
// a generated block that tracks the target for 'break' statements in a 'switch' statement
interface SwitchBlock extends CodeBlock {
interface SwitchBlock {
kind: CodeBlockKind.Switch;
isScript: boolean;
breakLabel: Label;
}
// a generated block that tracks the targets for 'break' and 'continue' statements, used for iteration statements
interface LoopBlock extends CodeBlock {
interface LoopBlock {
kind: CodeBlockKind.Loop;
continueLabel: Label;
isScript: boolean;
breakLabel: Label;
}
// a generated block associated with a 'with' statement
interface WithBlock extends CodeBlock {
interface WithBlock {
kind: CodeBlockKind.With;
expression: Identifier;
startLabel: Label;
endLabel: Label;
@ -2070,7 +2073,7 @@ namespace ts {
const startLabel = defineLabel();
const endLabel = defineLabel();
markLabel(startLabel);
beginBlock(<WithBlock>{
beginBlock({
kind: CodeBlockKind.With,
expression,
startLabel,
@ -2087,10 +2090,6 @@ namespace ts {
markLabel(block.endLabel);
}
function isWithBlock(block: CodeBlock): block is WithBlock {
return block.kind === CodeBlockKind.With;
}
/**
* Begins a code block for a generated `try` statement.
*/
@ -2098,7 +2097,7 @@ namespace ts {
const startLabel = defineLabel();
const endLabel = defineLabel();
markLabel(startLabel);
beginBlock(<ExceptionBlock>{
beginBlock({
kind: CodeBlockKind.Exception,
state: ExceptionBlockState.Try,
startLabel,
@ -2188,10 +2187,6 @@ namespace ts {
exception.state = ExceptionBlockState.Done;
}
function isExceptionBlock(block: CodeBlock): block is ExceptionBlock {
return block.kind === CodeBlockKind.Exception;
}
/**
* Begins a code block that supports `break` or `continue` statements that are defined in
* the source tree and not from generated code.
@ -2199,7 +2194,7 @@ namespace ts {
* @param labelText Names from containing labeled statements.
*/
function beginScriptLoopBlock(): void {
beginBlock(<LoopBlock>{
beginBlock({
kind: CodeBlockKind.Loop,
isScript: true,
breakLabel: -1,
@ -2217,7 +2212,7 @@ namespace ts {
*/
function beginLoopBlock(continueLabel: Label): Label {
const breakLabel = defineLabel();
beginBlock(<LoopBlock>{
beginBlock({
kind: CodeBlockKind.Loop,
isScript: false,
breakLabel,
@ -2245,7 +2240,7 @@ namespace ts {
*
*/
function beginScriptSwitchBlock(): void {
beginBlock(<SwitchBlock>{
beginBlock({
kind: CodeBlockKind.Switch,
isScript: true,
breakLabel: -1
@ -2259,7 +2254,7 @@ namespace ts {
*/
function beginSwitchBlock(): Label {
const breakLabel = defineLabel();
beginBlock(<SwitchBlock>{
beginBlock({
kind: CodeBlockKind.Switch,
isScript: false,
breakLabel,
@ -2280,7 +2275,7 @@ namespace ts {
}
function beginScriptLabeledBlock(labelText: string) {
beginBlock(<LabeledBlock>{
beginBlock({
kind: CodeBlockKind.Labeled,
isScript: true,
labelText,
@ -2290,7 +2285,7 @@ namespace ts {
function beginLabeledBlock(labelText: string) {
const breakLabel = defineLabel();
beginBlock(<LabeledBlock>{
beginBlock({
kind: CodeBlockKind.Labeled,
isScript: false,
labelText,
@ -2448,7 +2443,7 @@ namespace ts {
* @param location An optional source map location for the statement.
*/
function createInlineBreak(label: Label, location?: TextRange): ReturnStatement {
Debug.assert(label > 0, `Invalid label: ${label}`);
Debug.assertLessThan(0, label, "Invalid label");
return setTextRange(
createReturn(
createArrayLiteral([
@ -2878,34 +2873,37 @@ namespace ts {
for (; blockIndex < blockActions.length && blockOffsets[blockIndex] <= operationIndex; blockIndex++) {
const block = blocks[blockIndex];
const blockAction = blockActions[blockIndex];
if (isExceptionBlock(block)) {
if (blockAction === BlockAction.Open) {
if (!exceptionBlockStack) {
exceptionBlockStack = [];
}
switch (block.kind) {
case CodeBlockKind.Exception:
if (blockAction === BlockAction.Open) {
if (!exceptionBlockStack) {
exceptionBlockStack = [];
}
if (!statements) {
statements = [];
}
if (!statements) {
statements = [];
}
exceptionBlockStack.push(currentExceptionBlock);
currentExceptionBlock = block;
}
else if (blockAction === BlockAction.Close) {
currentExceptionBlock = exceptionBlockStack.pop();
}
}
else if (isWithBlock(block)) {
if (blockAction === BlockAction.Open) {
if (!withBlockStack) {
withBlockStack = [];
exceptionBlockStack.push(currentExceptionBlock);
currentExceptionBlock = block;
}
else if (blockAction === BlockAction.Close) {
currentExceptionBlock = exceptionBlockStack.pop();
}
break;
case CodeBlockKind.With:
if (blockAction === BlockAction.Open) {
if (!withBlockStack) {
withBlockStack = [];
}
withBlockStack.push(block);
}
else if (blockAction === BlockAction.Close) {
withBlockStack.pop();
}
withBlockStack.push(block);
}
else if (blockAction === BlockAction.Close) {
withBlockStack.pop();
}
break;
// default: do nothing
}
}
}

Просмотреть файл

@ -114,7 +114,7 @@ namespace ts {
compilerOptions.reactNamespace,
tagName,
objectProperties,
filter(map(children, transformJsxChildToExpression), isDefined),
mapDefined(children, transformJsxChildToExpression),
node,
location
);

Просмотреть файл

@ -151,7 +151,17 @@ namespace ts {
break;
}
recordEmittedDeclarationInScope(node);
// Record these declarations provided that they have a name.
if ((node as ClassDeclaration | FunctionDeclaration).name) {
recordEmittedDeclarationInScope(node as ClassDeclaration | FunctionDeclaration);
}
else {
// These nodes should always have names unless they are default-exports;
// however, class declaration parsing allows for undefined names, so syntactically invalid
// programs may also have an undefined name.
Debug.assert(node.kind === SyntaxKind.ClassDeclaration || hasModifier(node, ModifierFlags.Default));
}
break;
}
}
@ -2639,36 +2649,33 @@ namespace ts {
/**
* Records that a declaration was emitted in the current scope, if it was the first
* declaration for the provided symbol.
*
* NOTE: if there is ever a transformation above this one, we may not be able to rely
* on symbol names.
*/
function recordEmittedDeclarationInScope(node: Node) {
const name = node.symbol && node.symbol.escapedName;
if (name) {
if (!currentScopeFirstDeclarationsOfName) {
currentScopeFirstDeclarationsOfName = createUnderscoreEscapedMap<Node>();
}
function recordEmittedDeclarationInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration) {
if (!currentScopeFirstDeclarationsOfName) {
currentScopeFirstDeclarationsOfName = createUnderscoreEscapedMap<Node>();
}
if (!currentScopeFirstDeclarationsOfName.has(name)) {
currentScopeFirstDeclarationsOfName.set(name, node);
}
const name = declaredNameInScope(node);
if (!currentScopeFirstDeclarationsOfName.has(name)) {
currentScopeFirstDeclarationsOfName.set(name, node);
}
}
/**
* Determines whether a declaration is the first declaration with the same name emitted
* in the current scope.
* Determines whether a declaration is the first declaration with
* the same name emitted in the current scope.
*/
function isFirstEmittedDeclarationInScope(node: Node) {
function isFirstEmittedDeclarationInScope(node: ModuleDeclaration | EnumDeclaration) {
if (currentScopeFirstDeclarationsOfName) {
const name = node.symbol && node.symbol.escapedName;
if (name) {
return currentScopeFirstDeclarationsOfName.get(name) === node;
}
const name = declaredNameInScope(node);
return currentScopeFirstDeclarationsOfName.get(name) === node;
}
return true;
}
return false;
function declaredNameInScope(node: FunctionDeclaration | ClassDeclaration | ModuleDeclaration | EnumDeclaration): __String {
Debug.assertNode(node.name, isIdentifier);
return (node.name as Identifier).escapedText;
}
/**
@ -2746,7 +2753,7 @@ namespace ts {
return createNotEmittedStatement(node);
}
Debug.assert(isIdentifier(node.name), "TypeScript module should have an Identifier name.");
Debug.assertNode(node.name, isIdentifier, "A TypeScript namespace should have an Identifier name.");
enableSubstitutionForNamespaceExports();
const statements: Statement[] = [];

Просмотреть файл

@ -665,6 +665,8 @@ namespace ts {
}
}
ts.setStackTraceLimit();
if (ts.Debug.isDebugging) {
ts.Debug.enableDebugInfo();
}

Просмотреть файл

@ -999,6 +999,7 @@ namespace ts {
export interface StringLiteral extends LiteralExpression {
kind: SyntaxKind.StringLiteral;
/* @internal */ textSourceNode?: Identifier | StringLiteral | NumericLiteral; // Allows a StringLiteral to get its text from another node (used by transforms).
/* @internal */ singleQuote?: boolean;
}
// Note: 'brands' in our syntax nodes serve to give us a small amount of nominal typing.
@ -1808,7 +1809,7 @@ namespace ts {
export interface CatchClause extends Node {
kind: SyntaxKind.CatchClause;
parent?: TryStatement;
variableDeclaration: VariableDeclaration;
variableDeclaration?: VariableDeclaration;
block: Block;
}
@ -2176,16 +2177,18 @@ namespace ts {
locked?: boolean;
}
export interface AfterFinallyFlow extends FlowNode, FlowLock {
export interface AfterFinallyFlow extends FlowNodeBase, FlowLock {
antecedent: FlowNode;
}
export interface PreFinallyFlow extends FlowNode {
export interface PreFinallyFlow extends FlowNodeBase {
antecedent: FlowNode;
lock: FlowLock;
}
export interface FlowNode {
export type FlowNode =
| AfterFinallyFlow | PreFinallyFlow | FlowStart | FlowLabel | FlowAssignment | FlowCondition | FlowSwitchClause | FlowArrayMutation;
export interface FlowNodeBase {
flags: FlowFlags;
id?: number; // Node id used by flow type cache in checker
}
@ -2193,30 +2196,30 @@ namespace ts {
// FlowStart represents the start of a control flow. For a function expression or arrow
// function, the container property references the function (which in turn has a flowNode
// property for the containing control flow).
export interface FlowStart extends FlowNode {
export interface FlowStart extends FlowNodeBase {
container?: FunctionExpression | ArrowFunction | MethodDeclaration;
}
// FlowLabel represents a junction with multiple possible preceding control flows.
export interface FlowLabel extends FlowNode {
export interface FlowLabel extends FlowNodeBase {
antecedents: FlowNode[];
}
// FlowAssignment represents a node that assigns a value to a narrowable reference,
// i.e. an identifier or a dotted name that starts with an identifier or 'this'.
export interface FlowAssignment extends FlowNode {
export interface FlowAssignment extends FlowNodeBase {
node: Expression | VariableDeclaration | BindingElement;
antecedent: FlowNode;
}
// FlowCondition represents a condition that is known to be true or false at the
// node's location in the control flow.
export interface FlowCondition extends FlowNode {
export interface FlowCondition extends FlowNodeBase {
expression: Expression;
antecedent: FlowNode;
}
export interface FlowSwitchClause extends FlowNode {
export interface FlowSwitchClause extends FlowNodeBase {
switchStatement: SwitchStatement;
clauseStart: number; // Start index of case/default clause range
clauseEnd: number; // End index of case/default clause range
@ -2225,7 +2228,7 @@ namespace ts {
// FlowArrayMutation represents a node potentially mutates an array, i.e. an
// operation of the form 'x.push(value)', 'x.unshift(value)' or 'x[n] = value'.
export interface FlowArrayMutation extends FlowNode {
export interface FlowArrayMutation extends FlowNodeBase {
node: CallExpression | BinaryExpression;
antecedent: FlowNode;
}
@ -2255,6 +2258,17 @@ namespace ts {
}
/* @internal */
export interface RedirectInfo {
/** Source file this redirects to. */
readonly redirectTarget: SourceFile;
/**
* Source file for the duplicate package. This will not be used by the Program,
* but we need to keep this around so we can watch for changes in underlying.
*/
readonly unredirected: SourceFile;
}
// Source files are declarations when they are external modules.
export interface SourceFile extends Declaration {
kind: SyntaxKind.SourceFile;
@ -2265,6 +2279,13 @@ namespace ts {
/* @internal */ path: Path;
text: string;
/**
* If two source files are for the same version of the same package, one will redirect to the other.
* (See `createRedirectSourceFile` in program.ts.)
* The redirect will have this set. The other will not have anything set, but see Program#sourceFileIsRedirectedTo.
*/
/* @internal */ redirectInfo?: RedirectInfo | undefined;
amdDependencies: AmdDependency[];
moduleName: string;
referencedFiles: FileReference[];
@ -2435,6 +2456,11 @@ namespace ts {
/* @internal */ structureIsReused?: StructureIsReused;
/* @internal */ getSourceFileFromReference(referencingFile: SourceFile, ref: FileReference): SourceFile | undefined;
/** Given a source file, get the name of the package it was imported from. */
/* @internal */ sourceFileToPackageName: Map<string>;
/** Set of all source files that some other source file redirects to. */
/* @internal */ redirectTargetsSet: Map<true>;
}
/* @internal */
@ -2542,6 +2568,15 @@ namespace ts {
getSymbolsOfParameterPropertyDeclaration(parameter: ParameterDeclaration, parameterName: string): Symbol[];
getShorthandAssignmentValueSymbol(location: Node): Symbol | undefined;
getExportSpecifierLocalTargetSymbol(location: ExportSpecifier): Symbol | undefined;
/**
* If a symbol is a local symbol with an associated exported symbol, returns the exported symbol.
* Otherwise returns its input.
* For example, at `export type T = number;`:
* - `getSymbolAtLocation` at the location `T` will return the exported symbol for `T`.
* - But the result of `getSymbolsInScope` will contain the *local* symbol for `T`, not the exported symbol.
* - Calling `getExportSymbolOfSymbol` on that local symbol will return the exported symbol.
*/
getExportSymbolOfSymbol(symbol: Symbol): Symbol;
getPropertySymbolOfDestructuringAssignment(location: Identifier): Symbol | undefined;
getTypeAtLocation(node: Node): Type;
getTypeFromTypeNode(node: TypeNode): Type;
@ -2606,9 +2641,8 @@ namespace ts {
* Does not include properties of primitive types.
*/
/* @internal */ getAllPossiblePropertiesOfType(type: Type): Symbol[];
/* @internal */ resolveName(name: string, location: Node, meaning: SymbolFlags): Symbol | undefined;
/* @internal */ getJsxNamespace(): string;
/* @internal */ resolveNameAtLocation(location: Node, name: string, meaning: SymbolFlags): Symbol | undefined;
}
export enum NodeBuilderFlags {
@ -2683,11 +2717,12 @@ namespace ts {
UseFullyQualifiedType = 1 << 8, // Write out the fully qualified type name (eg. Module.Type, instead of Type)
InFirstTypeArgument = 1 << 9, // Writing first type argument of the instantiated type
InTypeAlias = 1 << 10, // Writing type in type alias declaration
UseTypeAliasValue = 1 << 11, // Serialize the type instead of using type-alias. This is needed when we emit declaration file.
SuppressAnyReturnType = 1 << 12, // If the return type is any-like, don't offer a return type.
AddUndefined = 1 << 13, // Add undefined to types of initialized, non-optional parameters
WriteClassExpressionAsTypeLiteral = 1 << 14, // Write a type literal instead of (Anonymous class)
InArrayType = 1 << 15, // Writing an array element type
UseAliasDefinedOutsideCurrentScope = 1 << 16, // For a `type T = ... ` defined in a different file, write `T` instead of its value,
// even though `T` can't be accessed in the current scope.
}
export const enum SymbolFormatFlags {
@ -2896,7 +2931,7 @@ namespace ts {
export interface Symbol {
flags: SymbolFlags; // Symbol flags
escapedName: __String; // Name of symbol
escapedName: __String; // Name of symbol
declarations?: Declaration[]; // Declarations associated with this symbol
valueDeclaration?: Declaration; // First value declaration of the symbol
members?: SymbolTable; // Class, interface or literal instance members
@ -3067,6 +3102,7 @@ namespace ts {
hasReportedStatementInAmbientContext?: boolean; // Cache boolean if we report statements in ambient context
jsxFlags?: JsxFlags; // flags for knowing what kind of element/attributes we're dealing with
resolvedJsxElementAttributesType?: Type; // resolved element attributes type of a JSX openinglike element
resolvedJsxElementAllAttributesType?: Type; // resolved all element attributes type of a JSX openinglike element
hasSuperCall?: boolean; // recorded result when we try to find super-call. We only try to find one if this flag is undefined, indicating that we haven't made an attempt.
superCall?: ExpressionStatement; // Cached first super-call found in the constructor. Used in checking whether super is called before this-accessing
switchTypes?: Type[]; // Cached array of switch case expression types
@ -3100,7 +3136,7 @@ namespace ts {
/* @internal */
ContainsObjectLiteral = 1 << 22, // Type is or contains object literal type
/* @internal */
ContainsAnyFunctionType = 1 << 23, // Type is or contains object literal type
ContainsAnyFunctionType = 1 << 23, // Type is or contains the anyFunctionType
NonPrimitive = 1 << 24, // intrinsic object type
/* @internal */
JsxAttributes = 1 << 25, // Jsx attributes type
@ -3317,6 +3353,11 @@ namespace ts {
awaitedTypeOfType?: Type;
}
/* @internal */
export interface SyntheticDefaultModuleType extends Type {
syntheticType?: Type;
}
export interface TypeVariable extends Type {
/* @internal */
resolvedBaseConstraint: Type;
@ -3425,11 +3466,29 @@ namespace ts {
AnyDefault = 1 << 2, // Infer anyType for no inferences (otherwise emptyObjectType)
}
/**
* Ternary values are defined such that
* x & y is False if either x or y is False.
* x & y is Maybe if either x or y is Maybe, but neither x or y is False.
* x & y is True if both x and y are True.
* x | y is False if both x and y are False.
* x | y is Maybe if either x or y is Maybe, but neither x or y is True.
* x | y is True if either x or y is True.
*/
export const enum Ternary {
False = 0,
Maybe = 1,
True = -1
}
export type TypeComparer = (s: Type, t: Type, reportErrors?: boolean) => Ternary;
/* @internal */
export interface InferenceContext extends TypeMapper {
signature: Signature; // Generic signature for which inferences are made
inferences: InferenceInfo[]; // Inferences made for each type parameter
flags: InferenceFlags; // Inference flags
compareTypes: TypeComparer; // Type comparer function
}
/* @internal */
@ -3559,6 +3618,7 @@ namespace ts {
paths?: MapLike<string[]>;
/*@internal*/ plugins?: PluginImport[];
preserveConstEnums?: boolean;
preserveSymlinks?: boolean;
project?: string;
/* @internal */ pretty?: DiagnosticStyle;
reactNamespace?: string;
@ -3874,6 +3934,10 @@ namespace ts {
readFile(fileName: string): string | undefined;
trace?(s: string): void;
directoryExists?(directoryName: string): boolean;
/**
* Resolve a symbolic link.
* @see https://nodejs.org/api/fs.html#fs_fs_realpathsync_path_options
*/
realpath?(path: string): string;
getCurrentDirectory?(): string;
getDirectories?(path: string): string[];
@ -3889,18 +3953,14 @@ namespace ts {
export interface ResolvedModule {
/** Path of the file the module was resolved to. */
resolvedFileName: string;
/**
* Denotes if 'resolvedFileName' is isExternalLibraryImport and thus should be a proper external module:
* - be a .d.ts file
* - use top level imports\exports
* - don't use tripleslash references
*/
/** True if `resolvedFileName` comes from `node_modules`. */
isExternalLibraryImport?: boolean;
}
/**
* ResolvedModule with an explicitly provided `extension` property.
* Prefer this over `ResolvedModule`.
* If changing this, remember to change `moduleResolutionIsEqualTo`.
*/
export interface ResolvedModuleFull extends ResolvedModule {
/**
@ -3908,6 +3968,22 @@ namespace ts {
* This is optional for backwards-compatibility, but will be added if not provided.
*/
extension: Extension;
packageId?: PackageId;
}
/**
* Unique identifier with a package name and version.
* If changing this, remember to change `packageIdIsEqual`.
*/
export interface PackageId {
/**
* Name of the package.
* Should not include `@types`.
* If accessing a non-index file, this should include its name e.g. "foo/bar".
*/
name: string;
/** Version of the package, e.g. "1.2.3" */
version: string;
}
export const enum Extension {
@ -4001,7 +4077,6 @@ namespace ts {
ContainsBindingPattern = 1 << 23,
ContainsYield = 1 << 24,
ContainsHoistedDeclarationOrCompletion = 1 << 25,
ContainsDynamicImport = 1 << 26,
// Please leave this as 1 << 29.

Просмотреть файл

@ -27,20 +27,6 @@ namespace ts {
return undefined;
}
export function findDeclaration<T extends Declaration>(symbol: Symbol, predicate: (node: Declaration) => node is T): T | undefined;
export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined;
export function findDeclaration(symbol: Symbol, predicate: (node: Declaration) => boolean): Declaration | undefined {
const declarations = symbol.declarations;
if (declarations) {
for (const declaration of declarations) {
if (predicate(declaration)) {
return declaration;
}
}
}
return undefined;
}
export interface StringSymbolWriter extends SymbolWriter {
string(): string;
}
@ -112,19 +98,21 @@ namespace ts {
sourceFile.resolvedTypeReferenceDirectiveNames.set(typeReferenceDirectiveName, resolvedTypeReferenceDirective);
}
/* @internal */
export function moduleResolutionIsEqualTo(oldResolution: ResolvedModuleFull, newResolution: ResolvedModuleFull): boolean {
return oldResolution.isExternalLibraryImport === newResolution.isExternalLibraryImport &&
oldResolution.extension === newResolution.extension &&
oldResolution.resolvedFileName === newResolution.resolvedFileName;
oldResolution.resolvedFileName === newResolution.resolvedFileName &&
packageIdIsEqual(oldResolution.packageId, newResolution.packageId);
}
function packageIdIsEqual(a: PackageId | undefined, b: PackageId | undefined): boolean {
return a === b || a && b && a.name === b.name && a.version === b.version;
}
/* @internal */
export function typeDirectiveIsEqualTo(oldResolution: ResolvedTypeReferenceDirective, newResolution: ResolvedTypeReferenceDirective): boolean {
return oldResolution.resolvedFileName === newResolution.resolvedFileName && oldResolution.primary === newResolution.primary;
}
/* @internal */
export function hasChangesInResolutions<T>(
names: ReadonlyArray<string>,
newResolutions: ReadonlyArray<T>,
@ -203,14 +191,6 @@ namespace ts {
return `${file.fileName}(${loc.line + 1},${loc.character + 1})`;
}
export function getStartPosOfNode(node: Node): number {
return node.pos;
}
export function isDefined(value: any): boolean {
return value !== undefined;
}
export function getEndLinePosition(line: number, sourceFile: SourceFileLike): number {
Debug.assert(line >= 0);
const lineStarts = getLineStarts(sourceFile);
@ -262,6 +242,32 @@ namespace ts {
return !nodeIsMissing(node);
}
/**
* Determine if the given comment is a triple-slash
*
* @return true if the comment is a triple-slash comment else false
*/
export function isRecognizedTripleSlashComment(text: string, commentPos: number, commentEnd: number) {
// Verify this is /// comment, but do the regexp match only when we first can find /// in the comment text
// so that we don't end up computing comment string and doing match for all // comments
if (text.charCodeAt(commentPos + 1) === CharacterCodes.slash &&
commentPos + 2 < commentEnd &&
text.charCodeAt(commentPos + 2) === CharacterCodes.slash) {
const textSubStr = text.substring(commentPos, commentEnd);
return textSubStr.match(fullTripleSlashReferencePathRegEx) ||
textSubStr.match(fullTripleSlashAMDReferencePathRegEx) ||
textSubStr.match(fullTripleSlashReferenceTypeReferenceDirectiveRegEx) ||
textSubStr.match(defaultLibReferenceRegEx) ?
true : false;
}
return false;
}
export function isPinnedComment(text: string, comment: CommentRange) {
return text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
}
export function getTokenPosOfNode(node: Node, sourceFile?: SourceFileLike, includeJsDoc?: boolean): number {
// With nodes that have no width (i.e. 'Missing' nodes), we actually *don't*
// want to skip trivia because this will launch us forward to the next token.
@ -338,15 +344,20 @@ namespace ts {
// or a (possibly escaped) quoted form of the original text if it's string-like.
switch (node.kind) {
case SyntaxKind.StringLiteral:
return '"' + escapeText(node.text) + '"';
if ((<StringLiteral>node).singleQuote) {
return "'" + escapeText(node.text, CharacterCodes.singleQuote) + "'";
}
else {
return '"' + escapeText(node.text, CharacterCodes.doubleQuote) + '"';
}
case SyntaxKind.NoSubstitutionTemplateLiteral:
return "`" + escapeText(node.text) + "`";
return "`" + escapeText(node.text, CharacterCodes.backtick) + "`";
case SyntaxKind.TemplateHead:
return "`" + escapeText(node.text) + "${";
return "`" + escapeText(node.text, CharacterCodes.backtick) + "${";
case SyntaxKind.TemplateMiddle:
return "}" + escapeText(node.text) + "${";
return "}" + escapeText(node.text, CharacterCodes.backtick) + "${";
case SyntaxKind.TemplateTail:
return "}" + escapeText(node.text) + "`";
return "}" + escapeText(node.text, CharacterCodes.backtick) + "`";
case SyntaxKind.NumericLiteral:
return node.text;
}
@ -437,6 +448,7 @@ namespace ts {
return isExternalModule(node) || compilerOptions.isolatedModules;
}
/* @internal */
export function isBlockScope(node: Node, parentNode: Node) {
switch (node.kind) {
case SyntaxKind.SourceFile:
@ -635,11 +647,7 @@ namespace ts {
}
export function getLeadingCommentRangesOfNode(node: Node, sourceFileOfNode: SourceFile) {
return getLeadingCommentRanges(sourceFileOfNode.text, node.pos);
}
export function getLeadingCommentRangesOfNodeFromText(node: Node, text: string) {
return getLeadingCommentRanges(text, node.pos);
return node.kind !== SyntaxKind.JsxText ? getLeadingCommentRanges(sourceFileOfNode.text, node.pos) : undefined;
}
export function getJSDocCommentRanges(node: Node, text: string) {
@ -649,7 +657,7 @@ namespace ts {
node.kind === SyntaxKind.ArrowFunction ||
node.kind === SyntaxKind.ParenthesizedExpression) ?
concatenate(getTrailingCommentRanges(text, node.pos), getLeadingCommentRanges(text, node.pos)) :
getLeadingCommentRangesOfNodeFromText(node, text);
getLeadingCommentRanges(text, node.pos);
// True if the comment starts with '/**' but not if it is '/**/'
return filter(commentRanges, comment =>
text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
@ -657,9 +665,10 @@ namespace ts {
text.charCodeAt(comment.pos + 3) !== CharacterCodes.slash);
}
export let fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
export let fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)('|")(.+?)\2.*?\/>/;
export let fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
export const fullTripleSlashReferencePathRegEx = /^(\/\/\/\s*<reference\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
const fullTripleSlashReferenceTypeReferenceDirectiveRegEx = /^(\/\/\/\s*<reference\s+types\s*=\s*)('|")(.+?)\2.*?\/>/;
export const fullTripleSlashAMDReferencePathRegEx = /^(\/\/\/\s*<amd-dependency\s+path\s*=\s*)('|")(.+?)\2.*?\/>/;
const defaultLibReferenceRegEx = /^(\/\/\/\s*<reference\s+no-default-lib\s*=\s*)('|")(.+?)\2\s*\/>/;
export function isPartOfTypeNode(node: Node): boolean {
if (SyntaxKind.FirstTypeNode <= node.kind && node.kind <= SyntaxKind.LastTypeNode) {
@ -922,21 +931,11 @@ namespace ts {
}
export function getContainingFunction(node: Node): FunctionLike {
while (true) {
node = node.parent;
if (!node || isFunctionLike(node)) {
return <FunctionLike>node;
}
}
return findAncestor(node.parent, isFunctionLike);
}
export function getContainingClass(node: Node): ClassLikeDeclaration {
while (true) {
node = node.parent;
if (!node || isClassLike(node)) {
return <ClassLikeDeclaration>node;
}
}
return findAncestor(node.parent, isClassLike);
}
export function getThisContainer(node: Node, includeArrowFunctions: boolean): Node {
@ -1509,8 +1508,8 @@ namespace ts {
parent.parent.kind === SyntaxKind.VariableStatement;
const variableStatementNode =
isInitializerOfVariableDeclarationInStatement ? parent.parent.parent :
isVariableOfVariableDeclarationStatement ? parent.parent :
undefined;
isVariableOfVariableDeclarationStatement ? parent.parent :
undefined;
if (variableStatementNode) {
getJSDocCommentsAndTagsWorker(variableStatementNode);
}
@ -1624,7 +1623,7 @@ namespace ts {
if (isInJavaScriptFile(node)) {
if (node.type && node.type.kind === SyntaxKind.JSDocVariadicType ||
forEach(getJSDocParameterTags(node),
t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) {
t => t.typeExpression && t.typeExpression.type.kind === SyntaxKind.JSDocVariadicType)) {
return true;
}
}
@ -1781,7 +1780,8 @@ namespace ts {
// Property name in binding element or import specifier
return (<BindingElement | ImportSpecifier>parent).propertyName === node;
case SyntaxKind.ExportSpecifier:
// Any name in an export specifier
case SyntaxKind.JsxAttribute:
// Any name in an export specifier or JSX Attribute
return true;
}
return false;
@ -1855,7 +1855,7 @@ namespace ts {
export function getFileReferenceFromReferencePath(comment: string, commentRange: CommentRange): ReferencePathMatchResult {
const simpleReferenceRegEx = /^\/\/\/\s*<reference\s+/gim;
const isNoDefaultLibRegEx = /^(\/\/\/\s*<reference\s+no-default-lib\s*=\s*)('|")(.+?)\2\s*\/>/gim;
const isNoDefaultLibRegEx = new RegExp(defaultLibReferenceRegEx.source, "gim");
if (simpleReferenceRegEx.test(comment)) {
if (isNoDefaultLibRegEx.test(comment)) {
return { isNoDefaultLib: true };
@ -2077,10 +2077,6 @@ namespace ts {
return getParseTreeNode(sourceFile, isSourceFile) || sourceFile;
}
export function getOriginalSourceFiles(sourceFiles: ReadonlyArray<SourceFile>) {
return sameMap(sourceFiles, getOriginalSourceFile);
}
export const enum Associativity {
Left,
Right
@ -2365,7 +2361,9 @@ namespace ts {
// the language service. These characters should be escaped when printing, and if any characters are added,
// the map below must be updated. Note that this regexp *does not* include the 'delete' character.
// There is no reason for this other than that JSON.stringify does not handle it either.
const escapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const doubleQuoteEscapedCharsRegExp = /[\\\"\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const singleQuoteEscapedCharsRegExp = /[\\\'\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const backtickQuoteEscapedCharsRegExp = /[\\\`\u0000-\u001f\t\v\f\b\r\n\u2028\u2029\u0085]/g;
const escapedCharsMap = createMapFromTemplate({
"\0": "\\0",
"\t": "\\t",
@ -2376,18 +2374,23 @@ namespace ts {
"\n": "\\n",
"\\": "\\\\",
"\"": "\\\"",
"\'": "\\\'",
"\`": "\\\`",
"\u2028": "\\u2028", // lineSeparator
"\u2029": "\\u2029", // paragraphSeparator
"\u0085": "\\u0085" // nextLine
});
/**
* Based heavily on the abstract 'Quote'/'QuoteJSONString' operation from ECMA-262 (24.3.2.2),
* but augmented for a few select characters (e.g. lineSeparator, paragraphSeparator, nextLine)
* Note that this doesn't actually wrap the input in double quotes.
*/
export function escapeString(s: string): string {
export function escapeString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string {
const escapedCharsRegExp =
quoteChar === CharacterCodes.backtick ? backtickQuoteEscapedCharsRegExp :
quoteChar === CharacterCodes.singleQuote ? singleQuoteEscapedCharsRegExp :
doubleQuoteEscapedCharsRegExp;
return s.replace(escapedCharsRegExp, getReplacement);
}
@ -2409,8 +2412,8 @@ namespace ts {
}
const nonAsciiCharacters = /[^\u0000-\u007F]/g;
export function escapeNonAsciiString(s: string): string {
s = escapeString(s);
export function escapeNonAsciiString(s: string, quoteChar?: CharacterCodes.doubleQuote | CharacterCodes.singleQuote | CharacterCodes.backtick): string {
s = escapeString(s, quoteChar);
// Replace non-ASCII characters with '\uNNNN' escapes if any exist.
// Otherwise just return the original string.
return nonAsciiCharacters.test(s) ?
@ -2832,7 +2835,7 @@ namespace ts {
//
// var x = 10;
if (node.pos === 0) {
leadingComments = filter(getLeadingCommentRanges(text, node.pos), isPinnedComment);
leadingComments = filter(getLeadingCommentRanges(text, node.pos), isPinnedCommentLocal);
}
}
else {
@ -2878,9 +2881,8 @@ namespace ts {
return currentDetachedCommentInfo;
function isPinnedComment(comment: CommentRange) {
return text.charCodeAt(comment.pos + 1) === CharacterCodes.asterisk &&
text.charCodeAt(comment.pos + 2) === CharacterCodes.exclamation;
function isPinnedCommentLocal(comment: CommentRange) {
return isPinnedComment(text, comment);
}
}
@ -2986,8 +2988,12 @@ namespace ts {
return getModifierFlags(node) !== ModifierFlags.None;
}
export function hasModifier(node: Node, flags: ModifierFlags) {
return (getModifierFlags(node) & flags) !== 0;
export function hasModifier(node: Node, flags: ModifierFlags): boolean {
return !!getSelectedModifierFlags(node, flags);
}
export function getSelectedModifierFlags(node: Node, flags: ModifierFlags): ModifierFlags {
return getModifierFlags(node) & flags;
}
export function getModifierFlags(node: Node): ModifierFlags {
@ -3073,24 +3079,6 @@ namespace ts {
return false;
}
// Returns false if this heritage clause element's expression contains something unsupported
// (i.e. not a name or dotted name).
export function isSupportedExpressionWithTypeArguments(node: ExpressionWithTypeArguments): boolean {
return isSupportedExpressionWithTypeArgumentsRest(node.expression);
}
function isSupportedExpressionWithTypeArgumentsRest(node: Expression): boolean {
if (node.kind === SyntaxKind.Identifier) {
return true;
}
else if (isPropertyAccessExpression(node)) {
return isSupportedExpressionWithTypeArgumentsRest(node.expression);
}
else {
return false;
}
}
export function isExpressionWithTypeArgumentsInClassExtendsClause(node: Node): boolean {
return tryGetClassExtendingExpressionWithTypeArguments(node) !== undefined;
}
@ -3227,81 +3215,6 @@ namespace ts {
return carriageReturnLineFeed;
}
/**
* Tests whether a node and its subtree is simple enough to have its position
* information ignored when emitting source maps in a destructuring assignment.
*
* @param node The expression to test.
*/
export function isSimpleExpression(node: Expression): boolean {
return isSimpleExpressionWorker(node, 0);
}
function isSimpleExpressionWorker(node: Expression, depth: number): boolean {
if (depth <= 5) {
const kind = node.kind;
if (kind === SyntaxKind.StringLiteral
|| kind === SyntaxKind.NumericLiteral
|| kind === SyntaxKind.RegularExpressionLiteral
|| kind === SyntaxKind.NoSubstitutionTemplateLiteral
|| kind === SyntaxKind.Identifier
|| kind === SyntaxKind.ThisKeyword
|| kind === SyntaxKind.SuperKeyword
|| kind === SyntaxKind.TrueKeyword
|| kind === SyntaxKind.FalseKeyword
|| kind === SyntaxKind.NullKeyword) {
return true;
}
else if (kind === SyntaxKind.PropertyAccessExpression) {
return isSimpleExpressionWorker((<PropertyAccessExpression>node).expression, depth + 1);
}
else if (kind === SyntaxKind.ElementAccessExpression) {
return isSimpleExpressionWorker((<ElementAccessExpression>node).expression, depth + 1)
&& isSimpleExpressionWorker((<ElementAccessExpression>node).argumentExpression, depth + 1);
}
else if (kind === SyntaxKind.PrefixUnaryExpression
|| kind === SyntaxKind.PostfixUnaryExpression) {
return isSimpleExpressionWorker((<PrefixUnaryExpression | PostfixUnaryExpression>node).operand, depth + 1);
}
else if (kind === SyntaxKind.BinaryExpression) {
return (<BinaryExpression>node).operatorToken.kind !== SyntaxKind.AsteriskAsteriskToken
&& isSimpleExpressionWorker((<BinaryExpression>node).left, depth + 1)
&& isSimpleExpressionWorker((<BinaryExpression>node).right, depth + 1);
}
else if (kind === SyntaxKind.ConditionalExpression) {
return isSimpleExpressionWorker((<ConditionalExpression>node).condition, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenTrue, depth + 1)
&& isSimpleExpressionWorker((<ConditionalExpression>node).whenFalse, depth + 1);
}
else if (kind === SyntaxKind.VoidExpression
|| kind === SyntaxKind.TypeOfExpression
|| kind === SyntaxKind.DeleteExpression) {
return isSimpleExpressionWorker((<VoidExpression | TypeOfExpression | DeleteExpression>node).expression, depth + 1);
}
else if (kind === SyntaxKind.ArrayLiteralExpression) {
return (<ArrayLiteralExpression>node).elements.length === 0;
}
else if (kind === SyntaxKind.ObjectLiteralExpression) {
return (<ObjectLiteralExpression>node).properties.length === 0;
}
else if (kind === SyntaxKind.CallExpression) {
if (!isSimpleExpressionWorker((<CallExpression>node).expression, depth + 1)) {
return false;
}
for (const argument of (<CallExpression>node).arguments) {
if (!isSimpleExpressionWorker(argument, depth + 1)) {
return false;
}
}
return true;
}
}
return false;
}
/**
* Formats an enum value as a string for debugging and debug assertions.
*/
@ -3374,24 +3287,6 @@ namespace ts {
return formatEnum(flags, (<any>ts).ObjectFlags, /*isFlags*/ true);
}
export function getRangePos(range: TextRange | undefined) {
return range ? range.pos : -1;
}
export function getRangeEnd(range: TextRange | undefined) {
return range ? range.end : -1;
}
/**
* Increases (or decreases) a position by the provided amount.
*
* @param pos The position.
* @param value The delta.
*/
export function movePos(pos: number, value: number) {
return positionIsSynthesized(pos) ? -1 : pos + value;
}
/**
* Creates a new TextRange from the provided pos and end.
*
@ -3449,26 +3344,6 @@ namespace ts {
return range.pos === range.end;
}
/**
* Creates a new TextRange from a provided range with its end position collapsed to its
* start position.
*
* @param range A TextRange.
*/
export function collapseRangeToStart(range: TextRange): TextRange {
return isCollapsedRange(range) ? range : moveRangeEnd(range, range.pos);
}
/**
* Creates a new TextRange from a provided range with its start position collapsed to its
* end position.
*
* @param range A TextRange.
*/
export function collapseRangeToEnd(range: TextRange): TextRange {
return isCollapsedRange(range) ? range : moveRangePos(range, range.end);
}
/**
* Creates a new TextRange for a token at the provides start position.
*
@ -3532,31 +3407,6 @@ namespace ts {
return node.initializer !== undefined;
}
/**
* Gets a value indicating whether a node is merged with a class declaration in the same scope.
*/
export function isMergedWithClass(node: Node) {
if (node.symbol) {
for (const declaration of node.symbol.declarations) {
if (declaration.kind === SyntaxKind.ClassDeclaration && declaration !== node) {
return true;
}
}
}
return false;
}
/**
* Gets a value indicating whether a node is the first declaration of its kind.
*
* @param node A Declaration node.
* @param kind The SyntaxKind to find among related declarations.
*/
export function isFirstDeclarationOfKind(node: Node, kind: SyntaxKind) {
return node.symbol && getDeclarationOfKind(node.symbol, kind) === node;
}
export function isWatchSet(options: CompilerOptions) {
// Firefox has Object.prototype.watch
return options.watch && options.hasOwnProperty("watch");
@ -3865,6 +3715,20 @@ namespace ts {
return hasModifier(node, ModifierFlags.ParameterPropertyModifier) && node.parent.kind === SyntaxKind.Constructor && isClassLike(node.parent.parent);
}
export function isEmptyBindingPattern(node: BindingName): node is BindingPattern {
if (isBindingPattern(node)) {
return every(node.elements, isEmptyBindingElement);
}
return false;
}
export function isEmptyBindingElement(node: BindingElement): boolean {
if (isOmittedExpression(node)) {
return true;
}
return isEmptyBindingPattern(node.name);
}
function walkUpBindingElementsAndPatterns(node: Node): Node {
while (node && (node.kind === SyntaxKind.BindingElement || isBindingPattern(node))) {
node = node.parent;
@ -5132,6 +4996,19 @@ namespace ts {
return isUnaryExpressionKind(skipPartiallyEmittedExpressions(node).kind);
}
/* @internal */
export function isUnaryExpressionWithWrite(expr: Node): expr is PrefixUnaryExpression | PostfixUnaryExpression {
switch (expr.kind) {
case SyntaxKind.PostfixUnaryExpression:
return true;
case SyntaxKind.PrefixUnaryExpression:
return (<PrefixUnaryExpression>expr).operator === SyntaxKind.PlusPlusToken ||
(<PrefixUnaryExpression>expr).operator === SyntaxKind.MinusMinusToken;
default:
return false;
}
}
function isExpressionKind(kind: SyntaxKind) {
return kind === SyntaxKind.ConditionalExpression
|| kind === SyntaxKind.YieldExpression
@ -5346,7 +5223,17 @@ namespace ts {
const kind = node.kind;
return isStatementKindButNotDeclarationKind(kind)
|| isDeclarationStatementKind(kind)
|| kind === SyntaxKind.Block;
|| isBlockStatement(node);
}
function isBlockStatement(node: Node): node is Block {
if (node.kind !== SyntaxKind.Block) return false;
if (node.parent !== undefined) {
if (node.parent.kind === SyntaxKind.TryStatement || node.parent.kind === SyntaxKind.CatchClause) {
return false;
}
}
return !isFunctionBlock(node);
}
// Module references

Просмотреть файл

@ -187,6 +187,9 @@ namespace FourSlash {
// The current caret position in the active file
public currentCaretPosition = 0;
// The position of the end of the current selection, or -1 if nothing is selected
public selectionEnd = -1;
public lastKnownMarker = "";
// The file that's currently 'opened'
@ -433,11 +436,19 @@ namespace FourSlash {
public goToPosition(pos: number) {
this.currentCaretPosition = pos;
this.selectionEnd = -1;
}
public select(startMarker: string, endMarker: string) {
const start = this.getMarkerByName(startMarker), end = this.getMarkerByName(endMarker);
this.goToPosition(start.position);
this.selectionEnd = end.position;
}
public moveCaretRight(count = 1) {
this.currentCaretPosition += count;
this.currentCaretPosition = Math.min(this.currentCaretPosition, this.getFileContent(this.activeFile.fileName).length);
this.selectionEnd = -1;
}
// Opens a file given its 0-based index or fileName
@ -451,7 +462,7 @@ namespace FourSlash {
this.languageServiceAdapterHost.openFile(fileToOpen.fileName, content, scriptKindName);
}
public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, negative: boolean) {
public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, shouldExist: boolean) {
const startMarker = this.getMarkerByName(startMarkerName);
const endMarker = this.getMarkerByName(endMarkerName);
const predicate = (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) =>
@ -459,9 +470,9 @@ namespace FourSlash {
const exists = this.anyErrorInRange(predicate, startMarker, endMarker);
if (exists !== negative) {
this.printErrorLog(negative, this.getAllDiagnostics());
throw new Error(`Failure between markers: '${startMarkerName}', '${endMarkerName}'`);
if (exists !== shouldExist) {
this.printErrorLog(shouldExist, this.getAllDiagnostics());
throw new Error(`${shouldExist ? "Expected" : "Did not expect"} failure between markers: '${startMarkerName}', '${endMarkerName}'`);
}
}
@ -483,10 +494,11 @@ namespace FourSlash {
}
private getAllDiagnostics(): ts.Diagnostic[] {
return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName => this.getDiagnostics(fileName));
return ts.flatMap(this.languageServiceAdapterHost.getFilenames(), fileName =>
ts.isAnySupportedFileExtension(fileName) ? this.getDiagnostics(fileName) : []);
}
public verifyErrorExistsAfterMarker(markerName: string, negative: boolean, after: boolean) {
public verifyErrorExistsAfterMarker(markerName: string, shouldExist: boolean, after: boolean) {
const marker: Marker = this.getMarkerByName(markerName);
let predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean;
@ -502,30 +514,15 @@ namespace FourSlash {
const exists = this.anyErrorInRange(predicate, marker);
const diagnostics = this.getAllDiagnostics();
if (exists !== negative) {
this.printErrorLog(negative, diagnostics);
throw new Error("Failure at marker: " + markerName);
if (exists !== shouldExist) {
this.printErrorLog(shouldExist, diagnostics);
throw new Error(`${shouldExist ? "Expected" : "Did not expect"} failure at marker '${markerName}'`);
}
}
private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean, startMarker: Marker, endMarker?: Marker) {
const errors = this.getDiagnostics(startMarker.fileName);
let exists = false;
const startPos = startMarker.position;
let endPos: number = undefined;
if (endMarker !== undefined) {
endPos = endMarker.position;
}
errors.forEach(function (error: ts.Diagnostic) {
if (predicate(error.start, error.start + error.length, startPos, endPos)) {
exists = true;
}
});
return exists;
private anyErrorInRange(predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean, startMarker: Marker, endMarker?: Marker): boolean {
return this.getDiagnostics(startMarker.fileName).some(({ start, length }) =>
predicate(start, start + length, startMarker.position, endMarker === undefined ? undefined : endMarker.position));
}
private printErrorLog(expectErrors: boolean, errors: ts.Diagnostic[]) {
@ -550,6 +547,7 @@ namespace FourSlash {
public verifyNoErrors() {
ts.forEachKey(this.inputFiles, fileName => {
if (!ts.isAnySupportedFileExtension(fileName)) return;
const errors = this.getDiagnostics(fileName);
if (errors.length) {
this.printErrorLog(/*expectErrors*/ false, errors);
@ -980,9 +978,9 @@ namespace FourSlash {
}
for (const reference of expectedReferences) {
const {fileName, start, end} = reference;
const { fileName, start, end } = reference;
if (reference.marker && reference.marker.data) {
const {isWriteAccess, isDefinition} = reference.marker.data;
const { isWriteAccess, isDefinition } = reference.marker.data;
this.verifyReferencesWorker(actualReferences, fileName, start, end, isWriteAccess, isDefinition);
}
else {
@ -1193,7 +1191,7 @@ namespace FourSlash {
displayParts: ts.SymbolDisplayPart[],
documentation: ts.SymbolDisplayPart[],
tags: ts.JSDocTagInfo[]
) {
) {
const actualQuickInfo = this.languageService.getQuickInfoAtPosition(this.activeFile.fileName, this.currentCaretPosition);
assert.equal(actualQuickInfo.kind, kind, this.messageAtLastKnownMarker("QuickInfo kind"));
@ -1789,19 +1787,16 @@ namespace FourSlash {
// We get back a set of edits, but langSvc.editScript only accepts one at a time. Use this to keep track
// of the incremental offset from each edit to the next. We assume these edit ranges don't overlap
edits = edits.sort((a, b) => a.span.start - b.span.start);
for (let i = 0; i < edits.length - 1; i++) {
const firstEditSpan = edits[i].span;
const firstEditEnd = firstEditSpan.start + firstEditSpan.length;
assert.isTrue(firstEditEnd <= edits[i + 1].span.start);
}
// Copy this so we don't ruin someone else's copy
edits = JSON.parse(JSON.stringify(edits));
// Get a snapshot of the content of the file so we can make sure any formatting edits didn't destroy non-whitespace characters
const oldContent = this.getFileContent(fileName);
let runningOffset = 0;
for (const edit of edits) {
const offsetStart = edit.span.start + runningOffset;
for (let i = 0; i < edits.length; i++) {
const edit = edits[i];
const offsetStart = edit.span.start;
const offsetEnd = offsetStart + edit.span.length;
this.editScriptAndUpdateMarkers(fileName, offsetStart, offsetEnd, edit.newText);
const editDelta = edit.newText.length - edit.span.length;
@ -1816,8 +1811,13 @@ namespace FourSlash {
}
}
runningOffset += editDelta;
// TODO: Consider doing this at least some of the time for higher fidelity. Currently causes a failure (bug 707150)
// this.languageService.getScriptLexicalStructure(fileName);
// Update positions of any future edits affected by this change
for (let j = i + 1; j < edits.length; j++) {
if (edits[j].span.start >= edits[i].span.start) {
edits[j].span.start += editDelta;
}
}
}
if (isFormattingEdit) {
@ -1901,7 +1901,7 @@ namespace FourSlash {
this.goToPosition(len);
}
public goToRangeStart({fileName, start}: Range) {
public goToRangeStart({ fileName, start }: Range) {
this.openFile(fileName);
this.goToPosition(start);
}
@ -2075,7 +2075,7 @@ namespace FourSlash {
return result;
}
private rangeText({fileName, start, end}: Range): string {
private rangeText({ fileName, start, end }: Range): string {
return this.getFileContent(fileName).slice(start, end);
}
@ -2361,7 +2361,7 @@ namespace FourSlash {
private applyCodeActions(actions: ts.CodeAction[], index?: number): void {
if (index === undefined) {
if (!(actions && actions.length === 1)) {
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : "" }`);
this.raiseError(`Should find exactly one codefix, but ${actions ? actions.length : "none"} found. ${actions ? actions.map(a => `${Harness.IO.newLine()} "${a.description}"`) : ""}`);
}
index = 0;
}
@ -2508,6 +2508,23 @@ namespace FourSlash {
}
}
public verifySpanOfEnclosingComment(negative: boolean, onlyMultiLineDiverges?: boolean) {
const expected = !negative;
const position = this.currentCaretPosition;
const fileName = this.activeFile.fileName;
const actual = !!this.languageService.getSpanOfEnclosingComment(fileName, position, /*onlyMultiLine*/ false);
const actualOnlyMultiLine = !!this.languageService.getSpanOfEnclosingComment(fileName, position, /*onlyMultiLine*/ true);
if (expected !== actual || onlyMultiLineDiverges === (actual === actualOnlyMultiLine)) {
this.raiseError(`verifySpanOfEnclosingComment failed:
position: '${position}'
fileName: '${fileName}'
onlyMultiLineDiverges: '${onlyMultiLineDiverges}'
actual: '${actual}'
actualOnlyMultiLine: '${actualOnlyMultiLine}'
expected: '${expected}'.`);
}
}
/*
Check number of navigationItems which match both searchValue and matchKind,
if a filename is passed in, limit the results to that file.
@ -2736,6 +2753,30 @@ namespace FourSlash {
}
}
private getSelection() {
return ({
pos: this.currentCaretPosition,
end: this.selectionEnd === -1 ? this.currentCaretPosition : this.selectionEnd
});
}
public verifyRefactorAvailable(negative: boolean, name?: string, subName?: string) {
const selection = this.getSelection();
let refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, selection) || [];
if (name) {
refactors = refactors.filter(r => r.name === name && (subName === undefined || r.actions.some(a => a.name === subName)));
}
const isAvailable = refactors.length > 0;
if (negative && isAvailable) {
this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected no refactor but found some: ${refactors.map(r => r.name).join(", ")}`);
}
else if (!negative && !isAvailable) {
this.raiseError(`verifyApplicableRefactorAvailableForRange failed - expected a refactor but found none.`);
}
}
public verifyApplicableRefactorAvailableForRange(negative: boolean) {
const ranges = this.getRanges();
if (!(ranges && ranges.length === 1)) {
@ -2752,6 +2793,20 @@ namespace FourSlash {
}
}
public applyRefactor(refactorName: string, actionName: string) {
const range = this.getSelection();
const refactors = this.languageService.getApplicableRefactors(this.activeFile.fileName, range);
const refactor = ts.find(refactors, r => r.name === refactorName);
if (!refactor) {
this.raiseError(`The expected refactor: ${refactorName} is not available at the marker location.`);
}
const editInfo = this.languageService.getEditsForRefactor(this.activeFile.fileName, this.formatCodeSettings, range, refactorName, actionName);
for (const edit of editInfo.edits) {
this.applyEdits(edit.fileName, edit.textChanges, /*isFormattingEdit*/ false);
}
}
public verifyFileAfterApplyingRefactorAtMarker(
markerName: string,
expectedContent: string,
@ -3496,6 +3551,10 @@ namespace FourSlashInterface {
public file(indexOrName: any, content?: string, scriptKindName?: string): void {
this.state.openFile(indexOrName, content, scriptKindName);
}
public select(startMarker: string, endMarker: string) {
this.state.select(startMarker, endMarker);
}
}
export class VerifyNegatable {
@ -3606,6 +3665,10 @@ namespace FourSlashInterface {
this.state.verifyBraceCompletionAtPosition(this.negative, openingBrace);
}
public isInCommentAtPosition(onlyMultiLineDiverges?: boolean) {
this.state.verifySpanOfEnclosingComment(this.negative, onlyMultiLineDiverges);
}
public codeFixAvailable() {
this.state.verifyCodeFixAvailable(this.negative);
}
@ -3617,6 +3680,10 @@ namespace FourSlashInterface {
public applicableRefactorAvailableForRange() {
this.state.verifyApplicableRefactorAvailableForRange(this.negative);
}
public refactorAvailable(name?: string, subName?: string) {
this.state.verifyRefactorAvailable(this.negative, name, subName);
}
}
export class Verify extends VerifyNegatable {
@ -4012,6 +4079,10 @@ namespace FourSlashInterface {
public disableFormatting() {
this.state.enableFormatting = false;
}
public applyRefactor(refactorName: string, actionName: string) {
this.state.applyRefactor(refactorName, actionName);
}
}
export class Debug {

Просмотреть файл

@ -67,17 +67,16 @@ namespace Utils {
export let currentExecutionEnvironment = getExecutionEnvironment();
const Buffer: typeof global.Buffer = currentExecutionEnvironment !== ExecutionEnvironment.Browser
? require("buffer").Buffer
: undefined;
// Thanks to browserify, Buffer is always available nowadays
const Buffer: typeof global.Buffer = require("buffer").Buffer;
export function encodeString(s: string): string {
return Buffer ? (new Buffer(s)).toString("utf8") : s;
return Buffer.from(s).toString("utf8");
}
export function byteLength(s: string, encoding?: string): number {
// stub implementation if Buffer is not available (in-browser case)
return Buffer ? Buffer.byteLength(s, encoding) : s.length;
return Buffer.byteLength(s, encoding);
}
export function evalFile(fileContents: string, fileName: string, nodeContext?: any) {
@ -133,17 +132,18 @@ namespace Utils {
return content;
}
export function memoize<T extends Function>(f: T): T {
const cache: { [idx: string]: any } = {};
export function memoize<T extends Function>(f: T, memoKey: (...anything: any[]) => string): T {
const cache = ts.createMap<any>();
return <any>(function(this: any) {
const key = Array.prototype.join.call(arguments);
const cachedResult = cache[key];
if (cachedResult) {
return cachedResult;
return <any>(function(this: any, ...args: any[]) {
const key = memoKey(...args);
if (cache.has(key)) {
return cache.get(key);
}
else {
return cache[key] = f.apply(this, arguments);
const value = f.apply(this, args);
cache.set(key, value);
return value;
}
});
}
@ -420,7 +420,7 @@ namespace Utils {
const maxHarnessFrames = 1;
export function filterStack(error: Error, stackTraceLimit: number = Infinity) {
export function filterStack(error: Error, stackTraceLimit = Infinity) {
const stack = <string>(<any>error).stack;
if (stack) {
const lines = stack.split(/\r\n?|\n/g);
@ -564,7 +564,7 @@ namespace Harness {
}
export let listFiles: typeof IO.listFiles = (path, spec?, options?) => {
options = options || <{ recursive?: boolean; }>{};
options = options || {};
function filesInFolder(folder: string): string[] {
let paths: string[] = [];
@ -686,7 +686,7 @@ namespace Harness {
return dirPath;
}
export let directoryName: typeof IO.directoryName = Utils.memoize(directoryNameImpl);
export let directoryName: typeof IO.directoryName = Utils.memoize(directoryNameImpl, path => path);
export function resolvePath(path: string) {
const response = Http.getFileFromServerSync(serverRoot + path + "?resolve=true");
@ -703,21 +703,22 @@ namespace Harness {
return response.status === 200;
}
export let listFiles = Utils.memoize((path: string, spec?: RegExp): string[] => {
export const listFiles = Utils.memoize((path: string, spec?: RegExp, options?: { recursive?: boolean }): string[] => {
const response = Http.getFileFromServerSync(serverRoot + path);
if (response.status === 200) {
const results = response.responseText.split(",");
let results = response.responseText.split(",");
if (spec) {
return results.filter(file => spec.test(file));
results = results.filter(file => spec.test(file));
}
else {
return results;
if (options && !options.recursive) {
results = results.filter(file => (ts.getDirectoryPath(ts.normalizeSlashes(file)) === path));
}
return results;
}
else {
return [""];
}
});
}, (path: string, spec?: RegExp, options?: { recursive?: boolean }) => `${path}|${spec}|${options ? options.recursive : undefined}`);
export function readFile(file: string): string | undefined {
const response = Http.getFileFromServerSync(serverRoot + file);
@ -1194,13 +1195,21 @@ namespace Harness {
return { result, options };
}
export function compileDeclarationFiles(inputFiles: TestFile[],
export interface DeclarationCompilationContext {
declInputFiles: TestFile[];
declOtherFiles: TestFile[];
harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions;
options: ts.CompilerOptions;
currentDirectory: string;
}
export function prepareDeclarationCompilationContext(inputFiles: TestFile[],
otherFiles: TestFile[],
result: CompilerResult,
harnessSettings: TestCaseParser.CompilerSettings & HarnessOptions,
options: ts.CompilerOptions,
// Current directory is needed for rwcRunner to be able to use currentDirectory defined in json file
currentDirectory: string) {
currentDirectory: string): DeclarationCompilationContext | undefined {
if (options.declaration && result.errors.length === 0 && result.declFilesCode.length !== result.files.length) {
throw new Error("There were no errors and declFiles generated did not match number of js files generated");
}
@ -1212,8 +1221,7 @@ namespace Harness {
if (options.declaration && result.errors.length === 0 && result.declFilesCode.length > 0) {
ts.forEach(inputFiles, file => addDtsFile(file, declInputFiles));
ts.forEach(otherFiles, file => addDtsFile(file, declOtherFiles));
const output = compileFiles(declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory || harnessSettings["currentDirectory"]);
return { declInputFiles, declOtherFiles, declResult: output.result };
return { declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory: currentDirectory || harnessSettings["currentDirectory"] };
}
function addDtsFile(file: TestFile, dtsFiles: TestFile[]) {
@ -1259,6 +1267,15 @@ namespace Harness {
}
}
export function compileDeclarationFiles(context: DeclarationCompilationContext | undefined) {
if (!context) {
return;
}
const { declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory } = context;
const output = compileFiles(declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory);
return { declInputFiles, declOtherFiles, declResult: output.result };
}
function normalizeLineEndings(text: string, lineEnding: string): string {
let normalized = text.replace(/\r\n?/g, "\n");
if (lineEnding !== "\n") {
@ -1273,10 +1290,19 @@ namespace Harness {
export function getErrorBaseline(inputFiles: TestFile[], diagnostics: ts.Diagnostic[]) {
diagnostics.sort(ts.compareDiagnostics);
const outputLines: string[] = [];
let outputLines = "";
// Count up all errors that were found in files other than lib.d.ts so we don't miss any
let totalErrorsReportedInNonLibraryFiles = 0;
let firstLine = true;
function newLine() {
if (firstLine) {
firstLine = false;
return "";
}
return "\r\n";
}
function outputErrorText(error: ts.Diagnostic) {
const message = ts.flattenDiagnosticMessageText(error.messageText, Harness.IO.newLine());
@ -1285,7 +1311,7 @@ namespace Harness {
.map(s => s.length > 0 && s.charAt(s.length - 1) === "\r" ? s.substr(0, s.length - 1) : s)
.filter(s => s.length > 0)
.map(s => "!!! " + ts.DiagnosticCategory[error.category].toLowerCase() + " TS" + error.code + ": " + s);
errLines.forEach(e => outputLines.push(e));
errLines.forEach(e => outputLines += (newLine() + e));
// do not count errors from lib.d.ts here, they are computed separately as numLibraryDiagnostics
// if lib.d.ts is explicitly included in input files and there are some errors in it (i.e. because of duplicate identifiers)
@ -1311,7 +1337,7 @@ namespace Harness {
// Header
outputLines.push("==== " + inputFile.unitName + " (" + fileErrors.length + " errors) ====");
outputLines += (newLine() + "==== " + inputFile.unitName + " (" + fileErrors.length + " errors) ====");
// Make sure we emit something for every error
let markedErrorCount = 0;
@ -1340,7 +1366,7 @@ namespace Harness {
nextLineStart = lineStarts[lineIndex + 1];
}
// Emit this line from the original file
outputLines.push(" " + line);
outputLines += (newLine() + " " + line);
fileErrors.forEach(err => {
// Does any error start or continue on to this line? Emit squiggles
const end = ts.textSpanEnd(err);
@ -1352,7 +1378,7 @@ namespace Harness {
// Calculate the start of the squiggle
const squiggleStart = Math.max(0, relativeOffset);
// TODO/REVIEW: this doesn't work quite right in the browser if a multi file test has files whose names are just the right length relative to one another
outputLines.push(" " + line.substr(0, squiggleStart).replace(/[^\s]/g, " ") + new Array(Math.min(length, line.length - squiggleStart) + 1).join("~"));
outputLines += (newLine() + " " + line.substr(0, squiggleStart).replace(/[^\s]/g, " ") + new Array(Math.min(length, line.length - squiggleStart) + 1).join("~"));
// If the error ended here, or we're at the end of the file, emit its message
if ((lineIndex === lines.length - 1) || nextLineStart > end) {
@ -1383,7 +1409,7 @@ namespace Harness {
assert.equal(totalErrorsReportedInNonLibraryFiles + numLibraryDiagnostics + numTest262HarnessDiagnostics, diagnostics.length, "total number of errors");
return minimalDiagnosticsToString(diagnostics) +
Harness.IO.newLine() + Harness.IO.newLine() + outputLines.join("\r\n");
Harness.IO.newLine() + Harness.IO.newLine() + outputLines;
}
export function doErrorBaseline(baselinePath: string, inputFiles: TestFile[], errors: ts.Diagnostic[]) {
@ -1586,9 +1612,10 @@ namespace Harness {
}
}
const declFileCompilationResult =
Harness.Compiler.compileDeclarationFiles(
toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined);
const declFileContext = Harness.Compiler.prepareDeclarationCompilationContext(
toBeCompiled, otherFiles, result, harnessSettings, options, /*currentDirectory*/ undefined
);
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declFileContext);
if (declFileCompilationResult && declFileCompilationResult.declResult.errors.length) {
jsCode += "\r\n\r\n//// [DtsFileErrors]\r\n";
@ -1963,7 +1990,7 @@ namespace Harness {
IO.writeFile(actualFileName + ".delete", "");
}
else {
IO.writeFile(actualFileName, actual);
IO.writeFile(actualFileName, encoded_actual);
}
throw new Error(`The baseline file ${relativeFileName} has changed.`);
}

Просмотреть файл

@ -193,7 +193,9 @@ namespace Harness.LanguageService {
}
getCurrentDirectory(): string { return virtualFileSystemRoot; }
getDefaultLibFileName(): string { return Harness.Compiler.defaultLibFileName; }
getScriptFileNames(): string[] { return this.getFilenames(); }
getScriptFileNames(): string[] {
return this.getFilenames().filter(ts.isAnySupportedFileExtension);
}
getScriptSnapshot(fileName: string): ts.IScriptSnapshot {
const script = this.getScriptInfo(fileName);
return script ? new ScriptSnapshot(script) : undefined;
@ -485,6 +487,9 @@ namespace Harness.LanguageService {
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean {
return unwrapJSONCallResult(this.shim.isValidBraceCompletionAtPosition(fileName, position, openingBrace));
}
getSpanOfEnclosingComment(fileName: string, position: number, onlyMultiLine: boolean): ts.TextSpan {
return unwrapJSONCallResult(this.shim.getSpanOfEnclosingComment(fileName, position, onlyMultiLine));
}
getCodeFixesAtPosition(): ts.CodeAction[] {
throw new Error("Not supported on the shim.");
}
@ -681,11 +686,11 @@ namespace Harness.LanguageService {
}
info(message: string): void {
return this.host.log(message);
this.host.log(message);
}
msg(message: string) {
return this.host.log(message);
msg(message: string): void {
this.host.log(message);
}
loggingEnabled() {
@ -700,17 +705,13 @@ namespace Harness.LanguageService {
return false;
}
endGroup(): void {
}
startGroup() { throw ts.notImplemented(); }
endGroup() { throw ts.notImplemented(); }
perftrc(message: string): void {
return this.host.log(message);
}
startGroup(): void {
}
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any {
return setTimeout(callback, ms, args);
}
@ -795,7 +796,7 @@ namespace Harness.LanguageService {
default:
return {
module: undefined,
error: "Could not resolve module"
error: new Error("Could not resolve module")
};
}
@ -828,6 +829,7 @@ namespace Harness.LanguageService {
host: serverHost,
cancellationToken: ts.server.nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,

Просмотреть файл

@ -426,12 +426,12 @@ class ProjectRunner extends RunnerBase {
compilerResult.program ?
ts.filter(compilerResult.program.getSourceFiles(), sourceFile => !Harness.isDefaultLibraryFile(sourceFile.fileName)) :
[]),
sourceFile => <Harness.Compiler.TestFile>{
(sourceFile): Harness.Compiler.TestFile => ({
unitName: ts.isRootedDiskPath(sourceFile.fileName) ?
RunnerBase.removeFullPaths(sourceFile.fileName) :
sourceFile.fileName,
content: sourceFile.text
});
}));
return Harness.Compiler.getErrorBaseline(inputFiles, compilerResult.errors);
}

Просмотреть файл

@ -54,7 +54,8 @@ namespace RWC {
useCustomLibraryFile = undefined;
});
it("can compile", () => {
it("can compile", function(this: Mocha.ITestCallbackContext) {
this.timeout(800000); // Allow long timeouts for RWC compilations
let opts: ts.ParsedCommandLine;
const ioLog: IOLog = JSON.parse(Harness.IO.readFile(jsonPath));
@ -89,9 +90,16 @@ namespace RWC {
ts.setConfigFileInOptions(opts.options, configParseResult.options.configFile);
}
// Load the files
// Deduplicate files so they are only printed once in baselines (they are deduplicated within the compiler already)
const uniqueNames = ts.createMap<true>();
for (const fileName of fileNames) {
inputFiles.push(getHarnessCompilerInputUnit(fileName));
// Must maintain order, build result list while checking map
const normalized = ts.normalizeSlashes(fileName);
if (!uniqueNames.has(normalized)) {
uniqueNames.set(normalized, true);
// Load the file
inputFiles.push(getHarnessCompilerInputUnit(fileName));
}
}
// Add files to compilation
@ -207,6 +215,14 @@ namespace RWC {
}, baselineOpts);
});
it("has the expected types", () => {
// We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols"
Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles
.concat(otherFiles)
.filter(file => !!compilerResult.program.getSourceFile(file.unitName))
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts);
});
// Ideally, a generated declaration file will have no errors. But we allow generated
// declaration file errors as part of the baseline.
it("has the expected errors in generated declaration files", () => {
@ -216,8 +232,12 @@ namespace RWC {
return null;
}
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory);
const declContext = Harness.Compiler.prepareDeclarationCompilationContext(
inputFiles, otherFiles, compilerResult, /*harnessSettings*/ undefined, compilerOptions, currentDirectory
);
// Reset compilerResult before calling into `compileDeclarationFiles` so the memory from the original compilation can be freed
compilerResult = undefined;
const declFileCompilationResult = Harness.Compiler.compileDeclarationFiles(declContext);
return Harness.Compiler.minimalDiagnosticsToString(declFileCompilationResult.declResult.errors) +
Harness.IO.newLine() + Harness.IO.newLine() +
@ -225,14 +245,6 @@ namespace RWC {
}, baselineOpts);
}
});
it("has the expected types", () => {
// We don't need to pass the extension here because "doTypeAndSymbolBaseline" will append appropriate extension of ".types" or ".symbols"
Harness.Compiler.doTypeAndSymbolBaseline(baseName, compilerResult, inputFiles
.concat(otherFiles)
.filter(file => !!compilerResult.program.getSourceFile(file.unitName))
.filter(e => !Harness.isDefaultLibraryFile(e.unitName)), baselineOpts);
});
});
}
}

Просмотреть файл

@ -74,11 +74,6 @@
"../services/formatting/tokenRange.ts",
"../services/codeFixProvider.ts",
"../services/codefixes/fixes.ts",
"../services/codefixes/fixExtendsInterfaceBecomesImplements.ts",
"../services/codefixes/fixClassIncorrectlyImplementsInterface.ts",
"../services/codefixes/fixClassDoesntImplementInheritedAbstractMember.ts",
"../services/codefixes/fixClassSuperMustPrecedeThisAccess.ts",
"../services/codefixes/fixConstructorForDerivedNeedSuperCall.ts",
"../services/codefixes/helpers.ts",
"../services/codefixes/importFixes.ts",
"../services/codefixes/fixUnusedIdentifier.ts",

Просмотреть файл

@ -52,23 +52,12 @@ namespace ts {
}
function createProject(rootFile: string, serverHost: server.ServerHost): { project: server.Project, rootScriptInfo: server.ScriptInfo } {
const logger: server.Logger = {
close: noop,
hasLevel: () => false,
loggingEnabled: () => false,
perftrc: noop,
info: noop,
startGroup: noop,
endGroup: noop,
msg: noop,
getLogFileName: (): string => undefined
};
const svcOpts: server.ProjectServiceOptions = {
host: serverHost,
logger,
logger: projectSystem.nullLogger,
cancellationToken: { isCancellationRequested: () => false },
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined
};
const projectService = new server.ProjectService(svcOpts);

Просмотреть файл

@ -36,6 +36,7 @@ namespace ts.projectSystem {
host,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: typingsInstaller || server.nullTypingsInstaller,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
@ -518,18 +519,20 @@ namespace ts.projectSystem {
};
const host = createServerHost([f], { newLine });
const session = createSession(host);
session.executeCommand(<server.protocol.OpenRequest>{
const openRequest: server.protocol.OpenRequest = {
seq: 1,
type: "request",
command: "open",
command: server.protocol.CommandTypes.Open,
arguments: { file: f.path }
});
session.executeCommand(<server.protocol.CompileOnSaveEmitFileRequest>{
};
session.executeCommand(openRequest);
const emitFileRequest: server.protocol.CompileOnSaveEmitFileRequest = {
seq: 2,
type: "request",
command: "compileOnSaveEmitFile",
command: server.protocol.CommandTypes.CompileOnSaveEmitFile,
arguments: { file: f.path }
});
};
session.executeCommand(emitFileRequest);
const emitOutput = host.readFile(path + ts.Extension.Js);
assert.equal(emitOutput, f.content + newLine, "content of emit output should be identical with the input + newline");
}
@ -550,7 +553,7 @@ namespace ts.projectSystem {
};
const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" });
const typingsInstaller = createTestTypingsInstaller(host);
const session = createSession(host, typingsInstaller);
const session = createSession(host, { typingsInstaller });
openFilesForSession([file1, file2], session);
const compileFileRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(CommandNames.CompileOnSaveEmitFile, { file: file1.path, projectFileName: configFile.path });

Просмотреть файл

@ -67,14 +67,14 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
@ -92,7 +92,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -100,7 +100,7 @@ namespace ts {
allowJs: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
@ -117,7 +117,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -146,7 +146,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
@ -174,7 +174,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
target: ScriptTarget.ES5,
noImplicitAny: false,
sourceMap: false,
@ -201,7 +201,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
noImplicitAny: false,
sourceMap: false,
},
@ -227,7 +227,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
noImplicitAny: false,
sourceMap: false,
},
@ -255,7 +255,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -286,7 +286,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -317,7 +317,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -348,7 +348,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -379,7 +379,7 @@ namespace ts {
}
}, "tsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
module: ModuleKind.CommonJS,
target: ScriptTarget.ES5,
noImplicitAny: false,
@ -415,8 +415,8 @@ namespace ts {
it("Convert default tsconfig.json to compiler-options ", () => {
assertCompilerOptions({}, "tsconfig.json",
{
compilerOptions: {} as CompilerOptions,
errors: <Diagnostic[]>[]
compilerOptions: {},
errors: []
}
);
});
@ -434,7 +434,7 @@ namespace ts {
}
}, "jsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
allowJs: true,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
@ -445,7 +445,7 @@ namespace ts {
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
@ -463,7 +463,7 @@ namespace ts {
}
}, "jsconfig.json",
{
compilerOptions: <CompilerOptions>{
compilerOptions: {
allowJs: false,
maxNodeModuleJsDepth: 2,
allowSyntheticDefaultImports: true,
@ -474,7 +474,7 @@ namespace ts {
sourceMap: false,
lib: ["lib.es5.d.ts", "lib.es2015.core.d.ts", "lib.es2015.symbol.d.ts"]
},
errors: <Diagnostic[]>[]
errors: []
}
);
});
@ -516,7 +516,7 @@ namespace ts {
allowSyntheticDefaultImports: true,
skipLibCheck: true
},
errors: <Diagnostic[]>[]
errors: []
}
);
});

Просмотреть файл

@ -0,0 +1,591 @@
/// <reference path="..\harness.ts" />
/// <reference path="tsserverProjectSystem.ts" />
namespace ts {
interface Range {
start: number;
end: number;
name: string;
}
interface Test {
source: string;
ranges: Map<Range>;
}
function extractTest(source: string): Test {
const activeRanges: Range[] = [];
let text = "";
let lastPos = 0;
let pos = 0;
const ranges = createMap<Range>();
while (pos < source.length) {
if (source.charCodeAt(pos) === CharacterCodes.openBracket &&
(source.charCodeAt(pos + 1) === CharacterCodes.hash || source.charCodeAt(pos + 1) === CharacterCodes.$)) {
const saved = pos;
pos += 2;
const s = pos;
consumeIdentifier();
const e = pos;
if (source.charCodeAt(pos) === CharacterCodes.bar) {
pos++;
text += source.substring(lastPos, saved);
const name = s === e
? source.charCodeAt(saved + 1) === CharacterCodes.hash ? "selection" : "extracted"
: source.substring(s, e);
activeRanges.push({ name, start: text.length, end: undefined });
lastPos = pos;
continue;
}
else {
pos = saved;
}
}
else if (source.charCodeAt(pos) === CharacterCodes.bar && source.charCodeAt(pos + 1) === CharacterCodes.closeBracket) {
text += source.substring(lastPos, pos);
activeRanges[activeRanges.length - 1].end = text.length;
const range = activeRanges.pop();
if (range.name in ranges) {
throw new Error(`Duplicate name of range ${range.name}`);
}
ranges.set(range.name, range);
pos += 2;
lastPos = pos;
continue;
}
pos++;
}
text += source.substring(lastPos, pos);
function consumeIdentifier() {
while (isIdentifierPart(source.charCodeAt(pos), ScriptTarget.Latest)) {
pos++;
}
}
return { source: text, ranges };
}
const newLineCharacter = "\n";
function getRuleProvider(action?: (opts: FormatCodeSettings) => void) {
const options = {
indentSize: 4,
tabSize: 4,
newLineCharacter,
convertTabsToSpaces: true,
indentStyle: ts.IndentStyle.Smart,
insertSpaceAfterConstructor: false,
insertSpaceAfterCommaDelimiter: true,
insertSpaceAfterSemicolonInForStatements: true,
insertSpaceBeforeAndAfterBinaryOperators: true,
insertSpaceAfterKeywordsInControlFlowStatements: true,
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBrackets: false,
insertSpaceAfterOpeningAndBeforeClosingNonemptyBraces: true,
insertSpaceAfterOpeningAndBeforeClosingTemplateStringBraces: false,
insertSpaceAfterOpeningAndBeforeClosingJsxExpressionBraces: false,
insertSpaceBeforeFunctionParenthesis: false,
placeOpenBraceOnNewLineForFunctions: false,
placeOpenBraceOnNewLineForControlBlocks: false,
};
if (action) {
action(options);
}
const rulesProvider = new formatting.RulesProvider();
rulesProvider.ensureUpToDate(options);
return rulesProvider;
}
function testExtractRangeFailed(caption: string, s: string, expectedErrors: string[]) {
return it(caption, () => {
const t = extractTest(s);
const file = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true);
const selectionRange = t.ranges.get("selection");
if (!selectionRange) {
throw new Error(`Test ${s} does not specify selection range`);
}
const result = refactor.extractMethod.getRangeToExtract(file, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
assert(result.targetRange === undefined, "failure expected");
const sortedErrors = result.errors.map(e => <string>e.messageText).sort();
assert.deepEqual(sortedErrors, expectedErrors.sort(), "unexpected errors");
});
}
function testExtractRange(s: string): void {
const t = extractTest(s);
const f = createSourceFile("a.ts", t.source, ScriptTarget.Latest, /*setParentNodes*/ true);
const selectionRange = t.ranges.get("selection");
if (!selectionRange) {
throw new Error(`Test ${s} does not specify selection range`);
}
const result = refactor.extractMethod.getRangeToExtract(f, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
const expectedRange = t.ranges.get("extracted");
if (expectedRange) {
let start: number, end: number;
if (ts.isArray(result.targetRange.range)) {
start = result.targetRange.range[0].getStart(f);
end = ts.lastOrUndefined(result.targetRange.range).getEnd();
}
else {
start = result.targetRange.range.getStart(f);
end = result.targetRange.range.getEnd();
}
assert.equal(start, expectedRange.start, "incorrect start of range");
assert.equal(end, expectedRange.end, "incorrect end of range");
}
else {
assert.isTrue(!result.targetRange, `expected range to extract to be undefined`);
}
}
describe("extractMethods", () => {
it("get extract range from selection", () => {
testExtractRange(`
[#|
[$|var x = 1;
var y = 2;|]|]
`);
testExtractRange(`
[#|
var x = 1;
var y = 2|];
`);
testExtractRange(`
[#|var x = 1|];
var y = 2;
`);
testExtractRange(`
if ([#|[#extracted|a && b && c && d|]|]) {
}
`);
testExtractRange(`
if [#|(a && b && c && d|]) {
}
`);
testExtractRange(`
if (a && b && c && d) {
[#| [$|var x = 1;
console.log(x);|] |]
}
`);
testExtractRange(`
[#|
if (a) {
return 100;
} |]
`);
testExtractRange(`
function foo() {
[#| [$|if (a) {
}
return 100|] |]
}
`);
testExtractRange(`
[#|
[$|l1:
if (x) {
break l1;
}|]|]
`);
testExtractRange(`
[#|
[$|l2:
{
if (x) {
}
break l2;
}|]|]
`);
testExtractRange(`
while (true) {
[#| if(x) {
}
break; |]
}
`);
testExtractRange(`
while (true) {
[#| if(x) {
}
continue; |]
}
`);
testExtractRange(`
l3:
{
[#|
if (x) {
}
break l3; |]
}
`);
testExtractRange(`
function f() {
while (true) {
[#|
if (x) {
return;
} |]
}
}
`);
testExtractRange(`
function f() {
while (true) {
[#|
[$|if (x) {
}
return;|]
|]
}
}
`);
testExtractRange(`
function f() {
return [#| [$|1 + 2|] |]+ 3;
}
}
`);
testExtractRange(`
function f() {
return [$|1 + [#|2 + 3|]|];
}
}
`);
testExtractRange(`
function f() {
return [$|1 + 2 + [#|3 + 4|]|];
}
}
`);
});
testExtractRangeFailed("extractRangeFailed1",
`
namespace A {
function f() {
[#|
let x = 1
if (x) {
return 10;
}
|]
}
}
`,
[
"Cannot extract range containing conditional return statement."
]);
testExtractRangeFailed("extractRangeFailed2",
`
namespace A {
function f() {
while (true) {
[#|
let x = 1
if (x) {
break;
}
|]
}
}
}
`,
[
"Cannot extract range containing conditional break or continue statements."
]);
testExtractRangeFailed("extractRangeFailed3",
`
namespace A {
function f() {
while (true) {
[#|
let x = 1
if (x) {
continue;
}
|]
}
}
}
`,
[
"Cannot extract range containing conditional break or continue statements."
]);
testExtractRangeFailed("extractRangeFailed4",
`
namespace A {
function f() {
l1: {
[#|
let x = 1
if (x) {
break l1;
}
|]
}
}
}
`,
[
"Cannot extract range containing labeled break or continue with target outside of the range."
]);
testExtractRangeFailed("extractRangeFailed5",
`
namespace A {
function f() {
[#|
try {
f2()
return 10;
}
catch (e) {
}
|]
}
function f2() {
}
}
`,
[
"Cannot extract range containing conditional return statement."
]);
testExtractRangeFailed("extractRangeFailed6",
`
namespace A {
function f() {
[#|
try {
f2()
}
catch (e) {
return 10;
}
|]
}
function f2() {
}
}
`,
[
"Cannot extract range containing conditional return statement."
]);
testExtractMethod("extractMethod1",
`namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
let a = 1;
[#|
let y = 5;
let z = x;
a = y;
foo();|]
}
}
}`);
testExtractMethod("extractMethod2",
`namespace A {
let x = 1;
function foo() {
}
namespace B {
function a() {
[#|
let y = 5;
let z = x;
return foo();|]
}
}
}`);
testExtractMethod("extractMethod3",
`namespace A {
function foo() {
}
namespace B {
function* a(z: number) {
[#|
let y = 5;
yield z;
return foo();|]
}
}
}`);
testExtractMethod("extractMethod4",
`namespace A {
function foo() {
}
namespace B {
async function a(z: number, z1: any) {
[#|
let y = 5;
if (z) {
await z1;
}
return foo();|]
}
}
}`);
testExtractMethod("extractMethod5",
`namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
[#|
let y = 5;
let z = x;
a = y;
foo();|]
}
}
}`);
testExtractMethod("extractMethod6",
`namespace A {
let x = 1;
export function foo() {
}
namespace B {
function a() {
let a = 1;
[#|
let y = 5;
let z = x;
a = y;
return foo();|]
}
}
}`);
testExtractMethod("extractMethod7",
`namespace A {
let x = 1;
export namespace C {
export function foo() {
}
}
namespace B {
function a() {
let a = 1;
[#|
let y = 5;
let z = x;
a = y;
return C.foo();|]
}
}
}`);
testExtractMethod("extractMethod8",
`namespace A {
let x = 1;
namespace B {
function a() {
let a1 = 1;
return 1 + [#|a1 + x|] + 100;
}
}
}`);
testExtractMethod("extractMethod9",
`namespace A {
export interface I { x: number };
namespace B {
function a() {
[#|let a1: I = { x: 1 };
return a1.x + 10;|]
}
}
}`);
testExtractMethod("extractMethod10",
`namespace A {
export interface I { x: number };
class C {
a() {
let z = 1;
[#|let a1: I = { x: 1 };
return a1.x + 10;|]
}
}
}`);
testExtractMethod("extractMethod11",
`namespace A {
let y = 1;
class C {
a() {
let z = 1;
[#|let a1 = { x: 1 };
y = 10;
z = 42;
return a1.x + 10;|]
}
}
}`);
testExtractMethod("extractMethod12",
`namespace A {
let y = 1;
class C {
b() {}
a() {
let z = 1;
[#|let a1 = { x: 1 };
y = 10;
z = 42;
this.b();
return a1.x + 10;|]
}
}
}`);
});
function testExtractMethod(caption: string, text: string) {
it(caption, () => {
Harness.Baseline.runBaseline(`extractMethod/${caption}.js`, () => {
const t = extractTest(text);
const selectionRange = t.ranges.get("selection");
if (!selectionRange) {
throw new Error(`Test ${caption} does not specify selection range`);
}
const f = {
path: "/a.ts",
content: t.source
};
const host = projectSystem.createServerHost([f]);
const projectService = projectSystem.createProjectService(host);
projectService.openClientFile(f.path);
const program = projectService.inferredProjects[0].getLanguageService().getProgram();
const sourceFile = program.getSourceFile(f.path);
const context: RefactorContext = {
cancellationToken: { throwIfCancellationRequested() { }, isCancellationRequested() { return false; } },
newLineCharacter,
program,
file: sourceFile,
startPosition: -1,
rulesProvider: getRuleProvider()
};
const result = refactor.extractMethod.getRangeToExtract(sourceFile, createTextSpanFromBounds(selectionRange.start, selectionRange.end));
assert.equal(result.errors, undefined, "expect no errors");
const results = refactor.extractMethod.getPossibleExtractions(result.targetRange, context);
const data: string[] = [];
data.push(`==ORIGINAL==`);
data.push(sourceFile.text);
for (const r of results) {
const changes = refactor.extractMethod.getPossibleExtractions(result.targetRange, context, results.indexOf(r))[0].changes;
data.push(`==SCOPE::${r.scopeDescription}==`);
data.push(textChanges.applyChanges(sourceFile.text, changes[0].textChanges));
}
return data.join(newLineCharacter);
});
});
}
}

Просмотреть файл

@ -73,6 +73,7 @@ namespace ts {
"c:/dev/a.d.ts",
"c:/dev/a.js",
"c:/dev/b.ts",
"c:/dev/x/a.ts",
"c:/dev/node_modules/a.ts",
"c:/dev/bower_components/a.ts",
"c:/dev/jspm_packages/a.ts"
@ -109,23 +110,21 @@ namespace ts {
}
{
const actual = ts.parseJsonConfigFileContent(json, host, basePath, existingOptions, configFileName, resolutionStack);
expected.errors = map(expected.errors, error => {
return <Diagnostic>{
category: error.category,
code: error.code,
file: undefined,
length: undefined,
messageText: error.messageText,
start: undefined,
};
});
expected.errors = expected.errors.map<Diagnostic>(error => ({
category: error.category,
code: error.code,
file: undefined,
length: undefined,
messageText: error.messageText,
start: undefined,
}));
assertParsed(actual, expected);
}
}
function createDiagnosticForConfigFile(json: any, start: number, length: number, diagnosticMessage: DiagnosticMessage, arg0: string) {
const text = JSON.stringify(json);
const file = <SourceFile>{
const file = <SourceFile>{ // tslint:disable-line no-object-literal-type-assertion
fileName: caseInsensitiveTsconfigPath,
kind: SyntaxKind.SourceFile,
text
@ -141,7 +140,8 @@ namespace ts {
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/b.ts"
"c:/dev/b.ts",
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
@ -462,7 +462,6 @@ namespace ts {
};
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
});
it("same named declarations are excluded", () => {
const json = {
include: [
@ -651,71 +650,127 @@ namespace ts {
};
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
});
it("with common package folders and no exclusions", () => {
const json = {
include: [
"**/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/bower_components/a.ts",
"c:/dev/jspm_packages/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("with common package folders and exclusions", () => {
const json = {
include: [
"**/a.ts"
],
exclude: [
"a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/bower_components/a.ts",
"c:/dev/jspm_packages/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("with common package folders and empty exclude", () => {
const json = {
include: [
"**/a.ts"
],
exclude: <string[]>[]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/bower_components/a.ts",
"c:/dev/jspm_packages/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
describe("with common package folders", () => {
it("and no exclusions", () => {
const json = {
include: [
"**/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and exclusions", () => {
const json = {
include: [
"**/?.ts"
],
exclude: [
"a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/b.ts",
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and empty exclude", () => {
const json = {
include: [
"**/a.ts"
],
exclude: <string[]>[]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and explicit recursive include", () => {
const json = {
include: [
"**/a.ts",
"**/node_modules/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/a.ts",
"c:/dev/x/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and wildcard include", () => {
const json = {
include: [
"*/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/x/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
it("and explicit wildcard include", () => {
const json = {
include: [
"*/a.ts",
"node_modules/a.ts"
]
};
const expected: ts.ParsedCommandLine = {
options: {},
errors: [],
fileNames: [
"c:/dev/x/a.ts",
"c:/dev/node_modules/a.ts"
],
wildcardDirectories: {
"c:/dev": ts.WatchDirectoryFlags.Recursive
},
};
validateMatches(expected, json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
});
});
it("exclude .js files when allowJs=false", () => {
const json = {
@ -1066,6 +1121,7 @@ namespace ts {
};
validateMatches(expected, json, caseInsensitiveHost, caseInsensitiveBasePath);
});
describe("with trailing recursive directory", () => {
it("in includes", () => {
const json = {
@ -1264,6 +1320,7 @@ namespace ts {
});
});
});
describe("with files or folders that begin with a .", () => {
it("that are not explicitly included", () => {
const json = {

Просмотреть файл

@ -26,10 +26,19 @@ namespace ts {
interface File {
name: string;
content?: string;
symlinks?: string[];
}
function createModuleResolutionHost(hasDirectoryExists: boolean, ...files: File[]): ModuleResolutionHost {
const map = arrayToMap(files, f => f.name);
const map = createMap<File>();
for (const file of files) {
map.set(file.name, file);
if (file.symlinks) {
for (const symlink of file.symlinks) {
map.set(symlink, file);
}
}
}
if (hasDirectoryExists) {
const directories = createMap<string>();
@ -46,6 +55,7 @@ namespace ts {
}
return {
readFile,
realpath,
directoryExists: path => directories.has(path),
fileExists: path => {
assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`);
@ -54,12 +64,15 @@ namespace ts {
};
}
else {
return { readFile, fileExists: path => map.has(path) };
return { readFile, realpath, fileExists: path => map.has(path) };
}
function readFile(path: string): string | undefined {
const file = map.get(path);
return file && file.content;
}
function realpath(path: string): string {
return map.get(path).name;
}
}
describe("Node module resolution - relative paths", () => {
@ -233,8 +246,8 @@ namespace ts {
test(/*hasDirectoryExists*/ true);
function test(hasDirectoryExists: boolean) {
const containingFile = { name: "/a/node_modules/b/c/node_modules/d/e.ts" };
const moduleFile = { name: "/a/node_modules/foo/index.d.ts" };
const containingFile: File = { name: "/a/node_modules/b/c/node_modules/d/e.ts" };
const moduleFile: File = { name: "/a/node_modules/foo/index.d.ts" };
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
"/a/node_modules/b/c/node_modules/d/node_modules/foo.ts",
@ -289,6 +302,19 @@ namespace ts {
]);
}
});
testPreserveSymlinks(/*preserveSymlinks*/ false);
testPreserveSymlinks(/*preserveSymlinks*/ true);
function testPreserveSymlinks(preserveSymlinks: boolean) {
it(`preserveSymlinks: ${preserveSymlinks}`, () => {
const realFileName = "/linked/index.d.ts";
const symlinkFileName = "/app/node_modulex/linked/index.d.ts";
const host = createModuleResolutionHost(/*hasDirectoryExists*/ true, { name: realFileName, symlinks: [symlinkFileName] });
const resolution = nodeModuleNameResolver("linked", "/app/app.ts", { preserveSymlinks }, host);
const resolvedFileName = preserveSymlinks ? symlinkFileName : realFileName;
checkResolvedModule(resolution.resolvedModule, { resolvedFileName, isExternalLibraryImport: true, extension: Extension.Dts });
});
}
});
describe("Module resolution - relative imports", () => {

Просмотреть файл

@ -4,12 +4,12 @@
namespace ts.projectSystem {
describe("Project errors", () => {
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: string[]) {
function checkProjectErrors(projectFiles: server.ProjectFilesWithTSDiagnostics, expectedErrors: ReadonlyArray<string>): void {
assert.isTrue(projectFiles !== undefined, "missing project files");
checkProjectErrorsWorker(projectFiles.projectErrors, expectedErrors);
}
function checkProjectErrorsWorker(errors: Diagnostic[], expectedErrors: string[]) {
function checkProjectErrorsWorker(errors: ReadonlyArray<Diagnostic>, expectedErrors: ReadonlyArray<string>): void {
assert.equal(errors ? errors.length : 0, expectedErrors.length, `expected ${expectedErrors.length} error in the list`);
if (expectedErrors.length) {
for (let i = 0; i < errors.length; i++) {

Просмотреть файл

@ -109,7 +109,10 @@ namespace ts {
function createTestCompilerHost(texts: NamedSourceText[], target: ScriptTarget, oldProgram?: ProgramWithSourceTexts): TestCompilerHost {
const files = arrayToMap(texts, t => t.name, t => {
if (oldProgram) {
const oldFile = <SourceFileWithText>oldProgram.getSourceFile(t.name);
let oldFile = <SourceFileWithText>oldProgram.getSourceFile(t.name);
if (oldFile && oldFile.redirectInfo) {
oldFile = oldFile.redirectInfo.unredirected;
}
if (oldFile && oldFile.sourceText.getVersion() === t.text.getVersion()) {
return oldFile;
}
@ -171,11 +174,16 @@ namespace ts {
return program;
}
function updateProgramText(files: ReadonlyArray<NamedSourceText>, fileName: string, newProgramText: string) {
const file = find(files, f => f.name === fileName)!;
file.text = file.text.updateProgram(newProgramText);
}
function checkResolvedTypeDirective(expected: ResolvedTypeReferenceDirective, actual: ResolvedTypeReferenceDirective): boolean {
if (!expected === !actual) {
if (expected) {
assert.isTrue(expected.resolvedFileName === actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`);
assert.isTrue(expected.primary === actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`);
assert.equal(expected.resolvedFileName, actual.resolvedFileName, `'resolvedFileName': expected '${expected.resolvedFileName}' to be equal to '${actual.resolvedFileName}'`);
assert.equal(expected.primary, actual.primary, `'primary': expected '${expected.primary}' to be equal to '${actual.primary}'`);
}
return true;
}
@ -238,7 +246,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
files[0].text = files[0].text.updateProgram("var x = 100");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
@ -249,7 +257,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], { target }, files => {
files[0].text = files[0].text.updateProgram("var x = 100");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
const program1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
const program2Diagnostics = program_2.getSemanticDiagnostics(program_1.getSourceFile("a.ts"));
assert.equal(program1Diagnostics.length, program2Diagnostics.length);
@ -263,19 +271,19 @@ namespace ts {
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if change affects type references", () => {
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
});
it("succeeds if change doesn't affect type references", () => {
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
});
it("fails if change affects imports", () => {
@ -283,7 +291,7 @@ namespace ts {
updateProgram(program_1, ["a.ts"], { target }, files => {
files[2].text = files[2].text.updateImportsAndExports("import x from 'b'");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if change affects type directives", () => {
@ -295,25 +303,25 @@ namespace ts {
/// <reference types="typerefs1" />`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_1.structureIsReused, StructureIsReused.SafeModules);
});
it("fails if module kind changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
});
it("fails if rootdir changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/b" });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
});
it("fails if config path changes", () => {
const program_1 = newProgram(files, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/b/tsconfig.json" });
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop);
assert.isTrue(program_1.structureIsReused === StructureIsReused.Not);
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
});
it("succeeds if missing files remain missing", () => {
@ -357,7 +365,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
// content of resolution cache should not change
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
@ -367,7 +375,7 @@ namespace ts {
const program_3 = updateProgram(program_2, ["a.ts"], options, files => {
files[0].text = files[0].text.updateImportsAndExports("");
});
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_2.structureIsReused, StructureIsReused.SafeModules);
checkResolvedModulesCache(program_3, "a.ts", /*expectedContent*/ undefined);
const program_4 = updateProgram(program_3, ["a.ts"], options, files => {
@ -376,7 +384,7 @@ namespace ts {
`;
files[0].text = files[0].text.updateImportsAndExports(newImports);
});
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_3.structureIsReused, StructureIsReused.SafeModules);
checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
});
@ -394,7 +402,7 @@ namespace ts {
const program_2 = updateProgram(program_1, ["/a.ts"], options, files => {
files[0].text = files[0].text.updateProgram("var x = 2");
});
assert.isTrue(program_1.structureIsReused === StructureIsReused.Completely);
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
// content of resolution cache should not change
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
@ -405,7 +413,7 @@ namespace ts {
files[0].text = files[0].text.updateReferences("");
});
assert.isTrue(program_2.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_2.structureIsReused, StructureIsReused.SafeModules);
checkResolvedTypeDirectivesCache(program_3, "/a.ts", /*expectedContent*/ undefined);
updateProgram(program_3, ["/a.ts"], options, files => {
@ -414,7 +422,7 @@ namespace ts {
`;
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(program_3.structureIsReused === StructureIsReused.SafeModules);
assert.equal(program_3.structureIsReused, StructureIsReused.SafeModules);
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
});
@ -454,7 +462,7 @@ namespace ts {
"initialProgram: execute module resolution normally.");
const initialProgramDiagnostics = initialProgram.getSemanticDiagnostics(initialProgram.getSourceFile("file1.ts"));
assert(initialProgramDiagnostics.length === 1, `initialProgram: import should fail.`);
assert.lengthOf(initialProgramDiagnostics, 1, `initialProgram: import should fail.`);
}
const afterNpmInstallProgram = updateProgram(initialProgram, rootFiles.map(f => f.name), options, f => {
@ -478,7 +486,7 @@ namespace ts {
"afterNpmInstallProgram: execute module resolution normally.");
const afterNpmInstallProgramDiagnostics = afterNpmInstallProgram.getSemanticDiagnostics(afterNpmInstallProgram.getSourceFile("file1.ts"));
assert(afterNpmInstallProgramDiagnostics.length === 0, `afterNpmInstallProgram: program is well-formed with import.`);
assert.lengthOf(afterNpmInstallProgramDiagnostics, 0, `afterNpmInstallProgram: program is well-formed with import.`);
}
});
@ -617,10 +625,10 @@ namespace ts {
"File 'f1.ts' exist - use it as a name resolution result.",
"======== Module name './f1' was successfully resolved to 'f1.ts'. ========"
],
"program_1: execute module reoslution normally.");
"program_1: execute module resolution normally.");
const program_1Diagnostics = program_1.getSemanticDiagnostics(program_1.getSourceFile("f2.ts"));
assert(program_1Diagnostics.length === expectedErrors, `initial program should be well-formed`);
assert.lengthOf(program_1Diagnostics, expectedErrors, `initial program should be well-formed`);
}
const indexOfF1 = 6;
const program_2 = updateProgram(program_1, program_1.getRootFileNames(), options, f => {
@ -630,7 +638,7 @@ namespace ts {
{
const program_2Diagnostics = program_2.getSemanticDiagnostics(program_2.getSourceFile("f2.ts"));
assert(program_2Diagnostics.length === expectedErrors, `removing no-default-lib shouldn't affect any types used.`);
assert.lengthOf(program_2Diagnostics, expectedErrors, `removing no-default-lib shouldn't affect any types used.`);
assert.deepEqual(program_2.host.getTrace(), [
"======== Resolving type reference directive 'typerefs1', containing file 'f1.ts', root directory 'node_modules/@types'. ========",
@ -659,7 +667,7 @@ namespace ts {
{
const program_3Diagnostics = program_3.getSemanticDiagnostics(program_3.getSourceFile("f2.ts"));
assert(program_3Diagnostics.length === expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`);
assert.lengthOf(program_3Diagnostics, expectedErrors, `typerefs2 was unused, so diagnostics should be unaffected.`);
assert.deepEqual(program_3.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
@ -684,7 +692,7 @@ namespace ts {
{
const program_4Diagnostics = program_4.getSemanticDiagnostics(program_4.getSourceFile("f2.ts"));
assert(program_4Diagnostics.length === expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`);
assert.lengthOf(program_4Diagnostics, expectedErrors, `a1.ts was unused, so diagnostics should be unaffected.`);
assert.deepEqual(program_4.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
@ -708,7 +716,7 @@ namespace ts {
{
const program_5Diagnostics = program_5.getSemanticDiagnostics(program_5.getSourceFile("f2.ts"));
assert(program_5Diagnostics.length === ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`);
assert.lengthOf(program_5Diagnostics, ++expectedErrors, `import of BB in f1 fails. BB is of type any. Add one error`);
assert.deepEqual(program_5.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
@ -725,7 +733,7 @@ namespace ts {
{
const program_6Diagnostics = program_6.getSemanticDiagnostics(program_6.getSourceFile("f2.ts"));
assert(program_6Diagnostics.length === expectedErrors, `import of BB in f1 fails.`);
assert.lengthOf(program_6Diagnostics, expectedErrors, `import of BB in f1 fails.`);
assert.deepEqual(program_6.host.getTrace(), [
"======== Resolving module './b1' from 'f1.ts'. ========",
@ -749,7 +757,7 @@ namespace ts {
{
const program_7Diagnostics = program_7.getSemanticDiagnostics(program_7.getSourceFile("f2.ts"));
assert(program_7Diagnostics.length === expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`);
assert.lengthOf(program_7Diagnostics, expectedErrors, `removing import is noop with respect to program, so no change in diagnostics.`);
assert.deepEqual(program_7.host.getTrace(), [
"======== Resolving type reference directive 'typerefs2', containing file 'f2.ts', root directory 'node_modules/@types'. ========",
@ -762,6 +770,98 @@ namespace ts {
], "program_7 should reuse module resolutions in f2 since it is unchanged");
}
});
describe("redirects", () => {
const axIndex = "/node_modules/a/node_modules/x/index.d.ts";
const axPackage = "/node_modules/a/node_modules/x/package.json";
const bxIndex = "/node_modules/b/node_modules/x/index.d.ts";
const bxPackage = "/node_modules/b/node_modules/x/package.json";
const root = "/a.ts";
const compilerOptions = { target, moduleResolution: ModuleResolutionKind.NodeJs };
function createRedirectProgram(options?: { bText: string, bVersion: string }): ProgramWithSourceTexts {
const files: NamedSourceText[] = [
{
name: "/node_modules/a/index.d.ts",
text: SourceText.New("", 'import X from "x";', "export function a(x: X): void;"),
},
{
name: axIndex,
text: SourceText.New("", "", "export default class X { private x: number; }"),
},
{
name: axPackage,
text: SourceText.New("", "", JSON.stringify({ name: "x", version: "1.2.3" })),
},
{
name: "/node_modules/b/index.d.ts",
text: SourceText.New("", 'import X from "x";', "export const b: X;"),
},
{
name: bxIndex,
text: SourceText.New("", "", options ? options.bText : "export default class X { private x: number; }"),
},
{
name: bxPackage,
text: SourceText.New("", "", JSON.stringify({ name: "x", version: options ? options.bVersion : "1.2.3" })),
},
{
name: root,
text: SourceText.New("", 'import { a } from "a"; import { b } from "b";', "a(b)"),
},
];
return newProgram(files, [root], compilerOptions);
}
function updateRedirectProgram(program: ProgramWithSourceTexts, updater: (files: NamedSourceText[]) => void): ProgramWithSourceTexts {
return updateProgram(program, [root], compilerOptions, updater);
}
it("No changes -> redirect not broken", () => {
const program_1 = createRedirectProgram();
const program_2 = updateRedirectProgram(program_1, files => {
updateProgramText(files, root, "const x = 1;");
});
assert.equal(program_1.structureIsReused, StructureIsReused.Completely);
assert.deepEqual(program_2.getSemanticDiagnostics(), emptyArray);
});
it("Target changes -> redirect broken", () => {
const program_1 = createRedirectProgram();
assert.deepEqual(program_1.getSemanticDiagnostics(), emptyArray);
const program_2 = updateRedirectProgram(program_1, files => {
updateProgramText(files, axIndex, "export default class X { private x: number; private y: number; }");
updateProgramText(files, axPackage, JSON.stringify('{ name: "x", version: "1.2.4" }'));
});
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
assert.lengthOf(program_2.getSemanticDiagnostics(), 1);
});
it("Underlying changes -> redirect broken", () => {
const program_1 = createRedirectProgram();
const program_2 = updateRedirectProgram(program_1, files => {
updateProgramText(files, bxIndex, "export default class X { private x: number; private y: number; }");
updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.4" }));
});
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
assert.lengthOf(program_2.getSemanticDiagnostics(), 1);
});
it("Previously duplicate packages -> program structure not reused", () => {
const program_1 = createRedirectProgram({ bVersion: "1.2.4", bText: "export = class X { private x: number; }" });
const program_2 = updateRedirectProgram(program_1, files => {
updateProgramText(files, bxIndex, "export default class X { private x: number; }");
updateProgramText(files, bxPackage, JSON.stringify({ name: "x", version: "1.2.3" }));
});
assert.equal(program_1.structureIsReused, StructureIsReused.Not);
assert.deepEqual(program_2.getSemanticDiagnostics(), []);
});
});
});
describe("host is optional", () => {

Просмотреть файл

@ -28,18 +28,6 @@ namespace ts.server {
createHash: Harness.LanguageService.mockHash,
};
const mockLogger: Logger = {
close: noop,
hasLevel(): boolean { return false; },
loggingEnabled(): boolean { return false; },
perftrc: noop,
info: noop,
startGroup: noop,
endGroup: noop,
msg: noop,
getLogFileName: (): string => undefined
};
class TestSession extends Session {
getProjectService() {
return this.projectService;
@ -55,10 +43,11 @@ namespace ts.server {
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: mockLogger,
logger: projectSystem.nullLogger,
canUseEvents: true
};
return new TestSession(opts);
@ -93,14 +82,15 @@ namespace ts.server {
session.executeCommand(req);
expect(lastSent).to.deep.equal(<protocol.Response>{
const expected: protocol.Response = {
command: CommandNames.Unknown,
type: "response",
seq: 0,
message: "Unrecognized JSON command: foobar",
request_seq: 0,
success: false
});
};
expect(lastSent).to.deep.equal(expected);
});
it("should return a tuple containing the response and if a response is required on success", () => {
const req: protocol.ConfigureRequest = {
@ -405,10 +395,11 @@ namespace ts.server {
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: mockLogger,
logger: projectSystem.nullLogger,
canUseEvents: true
});
this.addProtocolHandler(this.customHandler, () => {
@ -472,10 +463,11 @@ namespace ts.server {
host: mockHost,
cancellationToken: nullCancellationToken,
useSingleInferredProject: false,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: mockLogger,
logger: projectSystem.nullLogger,
canUseEvents: true
});
this.addProtocolHandler("echo", (req: protocol.Request) => ({

Просмотреть файл

@ -74,6 +74,70 @@ namespace ts {
}
}).outputText;
});
testBaseline("rewrittenNamespace", () => {
return ts.transpileModule(`namespace Reflect { const x = 1; }`, {
transformers: {
before: [forceNamespaceRewrite],
},
compilerOptions: {
newLine: NewLineKind.CarriageReturnLineFeed,
}
}).outputText;
});
testBaseline("rewrittenNamespaceFollowingClass", () => {
return ts.transpileModule(`
class C { foo = 10; static bar = 20 }
namespace C { export let x = 10; }
`, {
transformers: {
before: [forceNamespaceRewrite],
},
compilerOptions: {
target: ts.ScriptTarget.ESNext,
newLine: NewLineKind.CarriageReturnLineFeed,
}
}).outputText;
});
testBaseline("synthesizedClassAndNamespaceCombination", () => {
return ts.transpileModule("", {
transformers: {
before: [replaceWithClassAndNamespace],
},
compilerOptions: {
target: ts.ScriptTarget.ESNext,
newLine: NewLineKind.CarriageReturnLineFeed,
}
}).outputText;
function replaceWithClassAndNamespace() {
return (sourceFile: ts.SourceFile) => {
const result = getMutableClone(sourceFile);
result.statements = ts.createNodeArray([
ts.createClassDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, "Foo", /*typeParameters*/ undefined, /*heritageClauses*/ undefined, /*members*/ undefined),
ts.createModuleDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, createIdentifier("Foo"), createModuleBlock([createEmptyStatement()]))
]);
return result;
};
}
});
function forceNamespaceRewrite(context: ts.TransformationContext) {
return (sourceFile: ts.SourceFile): ts.SourceFile => {
return visitNode(sourceFile);
function visitNode<T extends ts.Node>(node: T): T {
if (node.kind === ts.SyntaxKind.ModuleBlock) {
const block = node as T & ts.ModuleBlock;
const statements = ts.createNodeArray([...block.statements]);
return ts.updateModuleBlock(block, statements) as typeof block;
}
return ts.visitEachChild(node, visitNode, context);
}
};
}
});
}

Просмотреть файл

@ -48,18 +48,18 @@ namespace ts.projectSystem {
}
export const nullLogger: server.Logger = {
close: () => void 0,
hasLevel: () => void 0,
close: noop,
hasLevel: () => false,
loggingEnabled: () => false,
perftrc: () => void 0,
info: () => void 0,
startGroup: () => void 0,
endGroup: () => void 0,
msg: () => void 0,
perftrc: noop,
info: noop,
msg: noop,
startGroup: noop,
endGroup: noop,
getLogFileName: (): string => undefined
};
export const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO);
const { content: libFileContent } = Harness.getDefaultLibraryFile(Harness.IO);
export const libFile: FileOrFolder = {
path: "/a/lib/lib.d.ts",
content: libFileContent
@ -132,7 +132,7 @@ namespace ts.projectSystem {
return JSON.stringify({ dependencies });
}
export function getExecutingFilePathFromLibFile(): string {
function getExecutingFilePathFromLibFile(): string {
return combinePaths(getDirectoryPath(libFile.path), "tsc.js");
}
@ -144,7 +144,7 @@ namespace ts.projectSystem {
return map(fileNames, toExternalFile);
}
export class TestServerEventManager {
class TestServerEventManager {
public events: server.ProjectServiceEvent[] = [];
handler: server.ProjectServiceEventHandler = (event: server.ProjectServiceEvent) => {
@ -157,7 +157,7 @@ namespace ts.projectSystem {
}
}
export interface TestServerHostCreationParameters {
interface TestServerHostCreationParameters {
useCaseSensitiveFileNames?: boolean;
executingFilePath?: string;
currentDirectory?: string;
@ -200,26 +200,31 @@ namespace ts.projectSystem {
}
}
export function createSession(host: server.ServerHost, typingsInstaller?: server.ITypingsInstaller, projectServiceEventHandler?: server.ProjectServiceEventHandler, cancellationToken?: server.ServerCancellationToken, throttleWaitMilliseconds?: number) {
if (typingsInstaller === undefined) {
typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/5, host);
export function createSession(host: server.ServerHost, opts: Partial<server.SessionOptions> = {}) {
if (opts.typingsInstaller === undefined) {
opts.typingsInstaller = new TestTypingsInstaller("/a/data/", /*throttleLimit*/ 5, host);
}
const opts: server.SessionOptions = {
if (opts.eventHandler !== undefined) {
opts.canUseEvents = true;
}
const sessionOptions: server.SessionOptions = {
host,
cancellationToken: cancellationToken || server.nullCancellationToken,
cancellationToken: server.nullCancellationToken,
useSingleInferredProject: false,
typingsInstaller,
useInferredProjectPerProjectRoot: false,
typingsInstaller: undefined,
byteLength: Utils.byteLength,
hrtime: process.hrtime,
logger: nullLogger,
canUseEvents: projectServiceEventHandler !== undefined,
eventHandler: projectServiceEventHandler,
throttleWaitMilliseconds
canUseEvents: false
};
return new TestSession(opts);
return new TestSession({ ...sessionOptions, ...opts });
}
export interface CreateProjectServiceParameters {
interface CreateProjectServiceParameters {
cancellationToken?: HostCancellationToken;
logger?: server.Logger;
useSingleInferredProject?: boolean;
@ -230,9 +235,17 @@ namespace ts.projectSystem {
export class TestProjectService extends server.ProjectService {
constructor(host: server.ServerHost, logger: server.Logger, cancellationToken: HostCancellationToken, useSingleInferredProject: boolean,
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler) {
typingsInstaller: server.ITypingsInstaller, eventHandler: server.ProjectServiceEventHandler, opts: Partial<server.ProjectServiceOptions> = {}) {
super({
host, logger, cancellationToken, useSingleInferredProject, typingsInstaller, eventHandler, typesMapLocation: customTypesMap.path
host,
logger,
cancellationToken,
useSingleInferredProject,
useInferredProjectPerProjectRoot: false,
typingsInstaller,
typesMapLocation: customTypesMap.path,
eventHandler,
...opts
});
}
@ -267,15 +280,15 @@ namespace ts.projectSystem {
entries: FSEntry[];
}
export function isFolder(s: FSEntry): s is Folder {
function isFolder(s: FSEntry): s is Folder {
return isArray((<Folder>s).entries);
}
export function isFile(s: FSEntry): s is File {
function isFile(s: FSEntry): s is File {
return typeof (<File>s).content === "string";
}
export function addFolder(fullPath: string, toPath: (s: string) => Path, fs: Map<FSEntry>): Folder {
function addFolder(fullPath: string, toPath: (s: string) => Path, fs: Map<FSEntry>): Folder {
const path = toPath(fullPath);
if (fs.has(path)) {
Debug.assert(isFolder(fs.get(path)));
@ -293,26 +306,29 @@ namespace ts.projectSystem {
return entry;
}
export function checkMapKeys(caption: string, map: Map<any>, expectedKeys: string[]) {
function checkMapKeys(caption: string, map: Map<any>, expectedKeys: string[]) {
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map`);
for (const name of expectedKeys) {
assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`);
}
}
export function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) {
assert.sameMembers(actualFileNames, expectedFileNames, caption);
function checkFileNames(caption: string, actualFileNames: string[], expectedFileNames: string[]) {
assert.equal(actualFileNames.length, expectedFileNames.length, `${caption}: incorrect actual number of files, expected ${JSON.stringify(expectedFileNames)}, got ${actualFileNames}`);
for (const f of expectedFileNames) {
assert.isTrue(contains(actualFileNames, f), `${caption}: expected to find ${f} in ${JSON.stringify(actualFileNames)}`);
}
}
export function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
function checkNumberOfConfiguredProjects(projectService: server.ProjectService, expected: number) {
assert.equal(projectService.configuredProjects.length, expected, `expected ${expected} configured project(s)`);
}
export function checkNumberOfExternalProjects(projectService: server.ProjectService, expected: number) {
function checkNumberOfExternalProjects(projectService: server.ProjectService, expected: number) {
assert.equal(projectService.externalProjects.length, expected, `expected ${expected} external project(s)`);
}
export function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) {
function checkNumberOfInferredProjects(projectService: server.ProjectService, expected: number) {
assert.equal(projectService.inferredProjects.length, expected, `expected ${expected} inferred project(s)`);
}
@ -326,7 +342,7 @@ namespace ts.projectSystem {
checkMapKeys("watchedFiles", host.watchedFiles, expectedFiles);
}
export function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[]) {
function checkWatchedDirectories(host: TestServerHost, expectedDirectories: string[]) {
checkMapKeys("watchedDirectories", host.watchedDirectories, expectedDirectories);
}
@ -334,11 +350,11 @@ namespace ts.projectSystem {
checkFileNames(`${server.ProjectKind[project.projectKind]} project, actual files`, project.getFileNames(), expectedFiles);
}
export function checkProjectRootFiles(project: server.Project, expectedFiles: string[]) {
function checkProjectRootFiles(project: server.Project, expectedFiles: string[]) {
checkFileNames(`${server.ProjectKind[project.projectKind]} project, rootFileNames`, project.getRootFiles(), expectedFiles);
}
export class Callbacks {
class Callbacks {
private map: TimeOutCallback[] = [];
private nextId = 1;
@ -374,7 +390,7 @@ namespace ts.projectSystem {
}
}
export type TimeOutCallback = () => any;
type TimeOutCallback = () => any;
export class TestServerHost implements server.ServerHost {
args: string[] = [];
@ -643,7 +659,7 @@ namespace ts.projectSystem {
}
}
describe("tsserver-project-system", () => {
describe("tsserverProjectSystem", () => {
const commonFile1: FileOrFolder = {
path: "/a/b/commonFile1.ts",
content: "let x = 1"
@ -2241,13 +2257,16 @@ namespace ts.projectSystem {
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
let lastEvent: server.ProjectLanguageServiceStateEvent;
const session = createSession(host, /*typingsInstaller*/ undefined, e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
return;
const session = createSession(host, {
canUseEvents: true,
eventHandler: e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ContextEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
return;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
assert.equal(e.data.project.getProjectName(), config.path, "project name");
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
assert.equal(e.data.project.getProjectName(), config.path, "project name");
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
});
session.executeCommand(<protocol.OpenRequest>{
seq: 0,
@ -2291,12 +2310,15 @@ namespace ts.projectSystem {
host.getFileSize = (filePath: string) =>
filePath === f2.path ? server.maxProgramSizeForNonTsFiles + 1 : originalGetFileSize.call(host, filePath);
let lastEvent: server.ProjectLanguageServiceStateEvent;
const session = createSession(host, /*typingsInstaller*/ undefined, e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
return;
const session = createSession(host, {
canUseEvents: true,
eventHandler: e => {
if (e.eventName === server.ConfigFileDiagEvent || e.eventName === server.ProjectInfoTelemetryEvent) {
return;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
}
assert.equal(e.eventName, server.ProjectLanguageServiceStateEvent);
lastEvent = <server.ProjectLanguageServiceStateEvent>e;
});
session.executeCommand(<protocol.OpenRequest>{
seq: 0,
@ -2342,6 +2364,50 @@ namespace ts.projectSystem {
const navbar = projectService.externalProjects[0].getLanguageService(/*ensureSynchronized*/ false).getNavigationBarItems(f1.path);
assert.equal(navbar[0].spans[0].length, f1.content.length);
});
it("deleting config file opened from the external project works", () => {
const site = {
path: "/user/someuser/project/js/site.js",
content: ""
};
const configFile = {
path: "/user/someuser/project/tsconfig.json",
content: "{}"
};
const projectFileName = "/user/someuser/project/WebApplication6.csproj";
const host = createServerHost([libFile, site, configFile]);
const projectService = createProjectService(host);
const externalProject: protocol.ExternalProject = {
projectFileName,
rootFiles: [toExternalFile(site.path), toExternalFile(configFile.path)],
options: { allowJs: false },
typeAcquisition: { "include": [] }
};
projectService.openExternalProjects([externalProject]);
let knownProjects = projectService.synchronizeProjectList([]);
checkNumberOfProjects(projectService, { configuredProjects: 1, externalProjects: 0, inferredProjects: 0 });
const configProject = projectService.configuredProjects[0];
checkProjectActualFiles(configProject, [libFile.path]);
const diagnostics = configProject.getAllProjectErrors();
assert.equal(diagnostics[0].code, Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
host.reloadFS([libFile, site]);
host.triggerFileWatcherCallback(configFile.path, FileWatcherEventKind.Deleted);
knownProjects = projectService.synchronizeProjectList(map(knownProjects, proj => proj.info));
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 0, inferredProjects: 0 });
externalProject.rootFiles.length = 1;
projectService.openExternalProjects([externalProject]);
checkNumberOfProjects(projectService, { configuredProjects: 0, externalProjects: 1, inferredProjects: 0 });
checkProjectActualFiles(projectService.externalProjects[0], [site.path, libFile.path]);
});
});
describe("Proper errors", () => {
@ -3036,7 +3102,10 @@ namespace ts.projectSystem {
};
const host = createServerHost([file, configFile]);
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
@ -3063,7 +3132,10 @@ namespace ts.projectSystem {
};
const host = createServerHost([file, configFile]);
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
});
@ -3082,7 +3154,10 @@ namespace ts.projectSystem {
};
const host = createServerHost([file, configFile]);
const session = createSession(host, /*typingsInstaller*/ undefined, serverEventManager.handler);
const session = createSession(host, {
canUseEvents: true,
eventHandler: serverEventManager.handler
});
openFilesForSession([file], session);
serverEventManager.checkEventCountOfType("configFileDiag", 1);
@ -3471,6 +3546,93 @@ namespace ts.projectSystem {
checkNumberOfProjects(projectService, { inferredProjects: 1 });
checkProjectActualFiles(projectService.inferredProjects[0], [f.path]);
});
it("inferred projects per project root", () => {
const file1 = { path: "/a/file1.ts", content: "let x = 1;", projectRootPath: "/a" };
const file2 = { path: "/a/file2.ts", content: "let y = 2;", projectRootPath: "/a" };
const file3 = { path: "/b/file2.ts", content: "let x = 3;", projectRootPath: "/b" };
const file4 = { path: "/c/file3.ts", content: "let z = 4;" };
const host = createServerHost([file1, file2, file3, file4]);
const session = createSession(host, {
useSingleInferredProject: true,
useInferredProjectPerProjectRoot: true
});
session.executeCommand(<server.protocol.SetCompilerOptionsForInferredProjectsRequest>{
seq: 1,
type: "request",
command: CommandNames.CompilerOptionsForInferredProjects,
arguments: {
options: {
allowJs: true,
target: ScriptTarget.ESNext
}
}
});
session.executeCommand(<server.protocol.SetCompilerOptionsForInferredProjectsRequest>{
seq: 2,
type: "request",
command: CommandNames.CompilerOptionsForInferredProjects,
arguments: {
options: {
allowJs: true,
target: ScriptTarget.ES2015
},
projectRootPath: "/b"
}
});
session.executeCommand(<server.protocol.OpenRequest>{
seq: 3,
type: "request",
command: CommandNames.Open,
arguments: {
file: file1.path,
fileContent: file1.content,
scriptKindName: "JS",
projectRootPath: file1.projectRootPath
}
});
session.executeCommand(<server.protocol.OpenRequest>{
seq: 4,
type: "request",
command: CommandNames.Open,
arguments: {
file: file2.path,
fileContent: file2.content,
scriptKindName: "JS",
projectRootPath: file2.projectRootPath
}
});
session.executeCommand(<server.protocol.OpenRequest>{
seq: 5,
type: "request",
command: CommandNames.Open,
arguments: {
file: file3.path,
fileContent: file3.content,
scriptKindName: "JS",
projectRootPath: file3.projectRootPath
}
});
session.executeCommand(<server.protocol.OpenRequest>{
seq: 6,
type: "request",
command: CommandNames.Open,
arguments: {
file: file4.path,
fileContent: file4.content,
scriptKindName: "JS"
}
});
const projectService = session.getProjectService();
checkNumberOfProjects(projectService, { inferredProjects: 3 });
checkProjectActualFiles(projectService.inferredProjects[0], [file4.path]);
checkProjectActualFiles(projectService.inferredProjects[1], [file1.path, file2.path]);
checkProjectActualFiles(projectService.inferredProjects[2], [file3.path]);
assert.equal(projectService.inferredProjects[0].getCompilerOptions().target, ScriptTarget.ESNext);
assert.equal(projectService.inferredProjects[1].getCompilerOptions().target, ScriptTarget.ESNext);
assert.equal(projectService.inferredProjects[2].getCompilerOptions().target, ScriptTarget.ES2015);
});
});
describe("No overwrite emit error", () => {
@ -3664,7 +3826,7 @@ namespace ts.projectSystem {
resetRequest: noop
};
const session = createSession(host, /*typingsInstaller*/ undefined, /*projectServiceEventHandler*/ undefined, cancellationToken);
const session = createSession(host, { cancellationToken });
expectedRequestId = session.getNextSeq();
session.executeCommandSeq(<server.protocol.OpenRequest>{
@ -3704,7 +3866,11 @@ namespace ts.projectSystem {
const cancellationToken = new TestServerCancellationToken();
const host = createServerHost([f1, config]);
const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken);
const session = createSession(host, {
canUseEvents: true,
eventHandler: () => { },
cancellationToken
});
{
session.executeCommandSeq(<protocol.OpenRequest>{
command: "open",
@ -3837,7 +4003,12 @@ namespace ts.projectSystem {
};
const cancellationToken = new TestServerCancellationToken(/*cancelAfterRequest*/ 3);
const host = createServerHost([f1, config]);
const session = createSession(host, /*typingsInstaller*/ undefined, () => { }, cancellationToken, /*throttleWaitMilliseconds*/ 0);
const session = createSession(host, {
canUseEvents: true,
eventHandler: () => { },
cancellationToken,
throttleWaitMilliseconds: 0
});
{
session.executeCommandSeq(<protocol.OpenRequest>{
command: "open",

Просмотреть файл

@ -935,25 +935,26 @@ namespace ts.projectSystem {
import * as cmd from "commander
`
};
session.executeCommand(<server.protocol.OpenRequest>{
const openRequest: server.protocol.OpenRequest = {
seq: 1,
type: "request",
command: "open",
command: server.protocol.CommandTypes.Open,
arguments: {
file: f.path,
fileContent: f.content
}
});
};
session.executeCommand(openRequest);
const projectService = session.getProjectService();
checkNumberOfProjects(projectService, { inferredProjects: 1 });
const proj = projectService.inferredProjects[0];
const version1 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
// make a change that should not affect the structure of the program
session.executeCommand(<server.protocol.ChangeRequest>{
const changeRequest: server.protocol.ChangeRequest = {
seq: 2,
type: "request",
command: "change",
command: server.protocol.CommandTypes.Change,
arguments: {
file: f.path,
insertString: "\nlet x = 1;",
@ -962,7 +963,8 @@ namespace ts.projectSystem {
endLine: 2,
endOffset: 0
}
});
};
session.executeCommand(changeRequest);
host.checkTimeoutQueueLength(1);
host.runQueuedTimeoutCallbacks();
const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
@ -1050,6 +1052,7 @@ namespace ts.projectSystem {
const result = JsTyping.discoverTypings(host, logger.log, [app.path, jquery.path, chroma.path], getDirectoryPath(<Path>app.path), safeList, emptyMap, { enable: true }, emptyArray);
assert.deepEqual(logger.finish(), [
'Inferred typings from file names: ["jquery","chroma-js"]',
"Inferred typings from unresolved imports: []",
'Result: {"cachedTypingPaths":[],"newTypingNames":["jquery","chroma-js"],"filesToWatch":["/a/b/bower_components","/a/b/node_modules"]}',
]);
assert.deepEqual(result.newTypingNames, ["jquery", "chroma-js"]);
@ -1114,6 +1117,8 @@ namespace ts.projectSystem {
const result = JsTyping.discoverTypings(host, logger.log, [app.path], getDirectoryPath(<Path>app.path), emptySafeList, cache, { enable: true }, /*unresolvedImports*/ []);
assert.deepEqual(logger.finish(), [
'Searching for typing names in /node_modules; all files: ["/node_modules/a/package.json"]',
' Found package names: ["a"]',
"Inferred typings from unresolved imports: []",
'Result: {"cachedTypingPaths":[],"newTypingNames":["a"],"filesToWatch":["/bower_components","/node_modules"]}',
]);
assert.deepEqual(result, {

Просмотреть файл

@ -49,6 +49,12 @@ var q:Point=<Point>p;`;
validateEditAtLineCharIndex = undefined;
});
it("handles empty lines array", () => {
const lineIndex = new server.LineIndex();
lineIndex.load([]);
assert.deepEqual(lineIndex.positionToLineOffset(0), { line: 1, offset: 1 });
});
it(`change 9 1 0 1 {"y"}`, () => {
validateEditAtLineCharIndex(9, 1, 0, "y");
});

2
src/lib/es2015.symbol.wellknown.d.ts поставляемый
Просмотреть файл

@ -110,7 +110,7 @@ interface Map<K, V> {
readonly [Symbol.toStringTag]: "Map";
}
interface WeakMap<K extends object, V>{
interface WeakMap<K extends object, V> {
readonly [Symbol.toStringTag]: "WeakMap";
}

10
src/lib/es5.d.ts поставляемый
Просмотреть файл

@ -216,7 +216,7 @@ interface ObjectConstructor {
* Returns the names of the enumerable properties and methods of an object.
* @param o Object that contains the properties and methods. This can be an object that you created or an existing Document Object Model (DOM) object.
*/
keys(o: any): string[];
keys(o: {}): string[];
}
/**
@ -980,12 +980,12 @@ interface ReadonlyArray<T> {
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[][]): T[];
concat(...items: ReadonlyArray<T>[]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | T[])[]): T[];
concat(...items: (T | ReadonlyArray<T>)[]): T[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.
@ -1099,12 +1099,12 @@ interface Array<T> {
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: T[][]): T[];
concat(...items: ReadonlyArray<T>[]): T[];
/**
* Combines two or more arrays.
* @param items Additional items to add to the end of array1.
*/
concat(...items: (T | T[])[]): T[];
concat(...items: (T | ReadonlyArray<T>)[]): T[];
/**
* Adds all the elements of an array separated by the specified separator string.
* @param separator A string used to separate one element of an array from the next in the resulting String. If omitted, the array elements are separated with a comma.

Просмотреть файл

@ -527,6 +527,10 @@ namespace ts.server {
return notImplemented();
}
getSpanOfEnclosingComment(_fileName: string, _position: number, _onlyMultiLine: boolean): TextSpan {
return notImplemented();
}
getCodeFixesAtPosition(file: string, start: number, end: number, errorCodes: number[]): CodeAction[] {
const args: protocol.CodeFixRequestArgs = { ...this.createFileRangeRequestArgs(file, start, end), errorCodes };

Просмотреть файл

@ -22,7 +22,7 @@ namespace ts.server {
export interface ConfigFileDiagEvent {
eventName: typeof ConfigFileDiagEvent;
data: { triggerFile: string, configFileName: string, diagnostics: Diagnostic[] };
data: { triggerFile: string, configFileName: string, diagnostics: ReadonlyArray<Diagnostic> };
}
export interface ProjectLanguageServiceStateEvent {
@ -205,7 +205,7 @@ namespace ts.server {
/**
* This helper function processes a list of projects and return the concatenated, sortd and deduplicated output of processing each project.
*/
export function combineProjectOutput<T>(projects: Project[], action: (project: Project) => T[], comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) {
export function combineProjectOutput<T>(projects: ReadonlyArray<Project>, action: (project: Project) => ReadonlyArray<T>, comparer?: (a: T, b: T) => number, areEqual?: (a: T, b: T) => boolean) {
const result = flatMap(projects, action).sort(comparer);
return projects.length > 1 ? deduplicate(result, areEqual) : result;
}
@ -225,14 +225,14 @@ namespace ts.server {
interface OpenConfigFileResult {
success: boolean;
errors?: Diagnostic[];
errors?: ReadonlyArray<Diagnostic>;
project?: ConfiguredProject;
}
export interface OpenConfiguredProjectResult {
configFileName?: NormalizedPath;
configFileErrors?: Diagnostic[];
configFileErrors?: ReadonlyArray<Diagnostic>;
}
interface FilePropertyReader<T> {
@ -328,11 +328,12 @@ namespace ts.server {
logger: Logger;
cancellationToken: HostCancellationToken;
useSingleInferredProject: boolean;
useInferredProjectPerProjectRoot: boolean;
typingsInstaller: ITypingsInstaller;
eventHandler?: ProjectServiceEventHandler;
throttleWaitMilliseconds?: number;
globalPlugins?: string[];
pluginProbeLocations?: string[];
globalPlugins?: ReadonlyArray<string>;
pluginProbeLocations?: ReadonlyArray<string>;
allowLocalPluginLoads?: boolean;
typesMapLocation?: string;
}
@ -370,7 +371,7 @@ namespace ts.server {
readonly openFiles: ScriptInfo[] = [];
private compilerOptionsForInferredProjects: CompilerOptions;
private compileOnSaveForInferredProjects: boolean;
private compilerOptionsForInferredProjectsPerProjectRoot = createMap<CompilerOptions>();
private readonly projectToSizeMap: Map<number> = createMap<number>();
private readonly directoryWatchers: DirectoryWatchers;
private readonly throttledOperations: ThrottledOperations;
@ -388,6 +389,7 @@ namespace ts.server {
public readonly logger: Logger;
public readonly cancellationToken: HostCancellationToken;
public readonly useSingleInferredProject: boolean;
public readonly useInferredProjectPerProjectRoot: boolean;
public readonly typingsInstaller: ITypingsInstaller;
public readonly throttleWaitMilliseconds?: number;
private readonly eventHandler?: ProjectServiceEventHandler;
@ -405,6 +407,7 @@ namespace ts.server {
this.logger = opts.logger;
this.cancellationToken = opts.cancellationToken;
this.useSingleInferredProject = opts.useSingleInferredProject;
this.useInferredProjectPerProjectRoot = opts.useInferredProjectPerProjectRoot;
this.typingsInstaller = opts.typingsInstaller || nullTypingsInstaller;
this.throttleWaitMilliseconds = opts.throttleWaitMilliseconds;
this.eventHandler = opts.eventHandler;
@ -453,19 +456,21 @@ namespace ts.server {
if (!this.eventHandler) {
return;
}
this.eventHandler(<ProjectLanguageServiceStateEvent>{
const event: ProjectLanguageServiceStateEvent = {
eventName: ProjectLanguageServiceStateEvent,
data: { project, languageServiceEnabled }
});
};
this.eventHandler(event);
}
private loadTypesMap() {
if (!this.host.fileExists(this.typesMapLocation)) {
this.logger.info(`Provided types map file "${this.typesMapLocation}" doesn't exist`);
return;
}
try {
const raw: TypesMapFile = JSON.parse(this.host.readFile(this.typesMapLocation));
const fileContent = this.host.readFile(this.typesMapLocation);
if (fileContent === undefined) {
this.logger.info(`Provided types map file "${this.typesMapLocation}" doesn't exist`);
return;
}
const raw: TypesMapFile = JSON.parse(fileContent);
// Parse the regexps
for (const k of Object.keys(raw.typesMap)) {
raw.typesMap[k].match = new RegExp(raw.typesMap[k].match as {} as string, "i");
@ -495,17 +500,42 @@ namespace ts.server {
project.updateGraph();
}
setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions): void {
this.compilerOptionsForInferredProjects = convertCompilerOptions(projectCompilerOptions);
setCompilerOptionsForInferredProjects(projectCompilerOptions: protocol.ExternalProjectCompilerOptions, projectRootPath?: string): void {
Debug.assert(projectRootPath === undefined || this.useInferredProjectPerProjectRoot, "Setting compiler options per project root path is only supported when useInferredProjectPerProjectRoot is enabled");
const compilerOptions = convertCompilerOptions(projectCompilerOptions);
// always set 'allowNonTsExtensions' for inferred projects since user cannot configure it from the outside
// previously we did not expose a way for user to change these settings and this option was enabled by default
this.compilerOptionsForInferredProjects.allowNonTsExtensions = true;
this.compileOnSaveForInferredProjects = projectCompilerOptions.compileOnSave;
for (const proj of this.inferredProjects) {
proj.setCompilerOptions(this.compilerOptionsForInferredProjects);
proj.compileOnSaveEnabled = projectCompilerOptions.compileOnSave;
compilerOptions.allowNonTsExtensions = true;
if (projectRootPath) {
this.compilerOptionsForInferredProjectsPerProjectRoot.set(projectRootPath, compilerOptions);
}
this.updateProjectGraphs(this.inferredProjects);
else {
this.compilerOptionsForInferredProjects = compilerOptions;
}
const updatedProjects: Project[] = [];
for (const project of this.inferredProjects) {
// Only update compiler options in the following cases:
// - Inferred projects without a projectRootPath, if the new options do not apply to
// a workspace root
// - Inferred projects with a projectRootPath, if the new options do not apply to a
// workspace root and there is no more specific set of options for that project's
// root path
// - Inferred projects with a projectRootPath, if the new options apply to that
// project root path.
if (projectRootPath ?
project.projectRootPath === projectRootPath :
!project.projectRootPath || !this.compilerOptionsForInferredProjectsPerProjectRoot.has(project.projectRootPath)) {
project.setCompilerOptions(compilerOptions);
project.compileOnSaveEnabled = compilerOptions.compileOnSave;
updatedProjects.push(project);
}
}
this.updateProjectGraphs(updatedProjects);
}
stopWatchingDirectory(directory: string) {
@ -634,10 +664,11 @@ namespace ts.server {
}
for (const openFile of this.openFiles) {
this.eventHandler(<ContextEvent>{
const event: ContextEvent = {
eventName: ContextEvent,
data: { project: openFile.getDefaultProject(), fileName: openFile.fileName }
});
};
this.eventHandler(event);
}
}
@ -745,7 +776,7 @@ namespace ts.server {
}
}
private assignScriptInfoToInferredProjectIfNecessary(info: ScriptInfo, addToListOfOpenFiles: boolean): void {
private assignScriptInfoToInferredProjectIfNecessary(info: ScriptInfo, addToListOfOpenFiles: boolean, projectRootPath?: string): void {
const externalProject = this.findContainingExternalProject(info.fileName);
if (externalProject) {
// file is already included in some external project - do nothing
@ -773,30 +804,30 @@ namespace ts.server {
}
if (info.containingProjects.length === 0) {
// create new inferred project p with the newly opened file as root
// or add root to existing inferred project if 'useOneInferredProject' is true
const inferredProject = this.createInferredProjectWithRootFileIfNecessary(info);
if (!this.useSingleInferredProject) {
// if useOneInferredProject is not set then try to fixup ownership of open files
// check 'defaultProject !== inferredProject' is necessary to handle cases
// when creation inferred project for some file has added other open files into this project (i.e. as referenced files)
// we definitely don't want to delete the project that was just created
// get (or create) an inferred project using the newly opened file as a root.
const inferredProject = this.createInferredProjectWithRootFileIfNecessary(info, projectRootPath);
if (!this.useSingleInferredProject && !inferredProject.projectRootPath) {
// if useSingleInferredProject is false and the inferred project is not associated
// with a project root, then try to repair the ownership of open files.
for (const f of this.openFiles) {
if (f.containingProjects.length === 0 || !inferredProject.containsScriptInfo(f)) {
// this is orphaned file that we have not processed yet - skip it
continue;
}
for (const fContainingProject of f.containingProjects) {
if (fContainingProject.projectKind === ProjectKind.Inferred &&
fContainingProject.isRoot(f) &&
fContainingProject !== inferredProject) {
for (const containingProject of f.containingProjects) {
// We verify 'containingProject !== inferredProject' to handle cases
// where the inferred project for some file has added other open files
// into this project (i.e. as referenced files) as we don't want to
// delete the project that was just created
if (containingProject.projectKind === ProjectKind.Inferred &&
containingProject !== inferredProject &&
containingProject.isRoot(f)) {
// open file used to be root in inferred project,
// this inferred project is different from the one we've just created for current file
// and new inferred project references this open file.
// We should delete old inferred project and attach open file to the new one
this.removeProject(fContainingProject);
this.removeProject(containingProject);
f.attachToProject(inferredProject);
}
}
@ -961,11 +992,20 @@ namespace ts.server {
}
this.logger.startGroup();
let counter = 0;
counter = printProjects(this.logger, this.externalProjects, counter);
counter = printProjects(this.logger, this.configuredProjects, counter);
counter = printProjects(this.logger, this.inferredProjects, counter);
const printProjects = (projects: Project[], counter: number): number => {
for (const project of projects) {
project.updateGraph();
this.logger.info(`Project '${project.getProjectName()}' (${ProjectKind[project.projectKind]}) ${counter}`);
this.logger.info(project.filesToString());
this.logger.info("-----------------------------------------------");
counter++;
}
return counter;
};
counter = printProjects(this.externalProjects, counter);
counter = printProjects(this.configuredProjects, counter);
printProjects(this.inferredProjects, counter);
this.logger.info("Open files: ");
for (const rootFile of this.openFiles) {
@ -973,17 +1013,6 @@ namespace ts.server {
}
this.logger.endGroup();
function printProjects(logger: Logger, projects: Project[], counter: number) {
for (const project of projects) {
project.updateGraph();
logger.info(`Project '${project.getProjectName()}' (${ProjectKind[project.projectKind]}) ${counter}`);
logger.info(project.filesToString());
logger.info("-----------------------------------------------");
counter++;
}
return counter;
}
}
private findConfiguredProjectByProjectName(configFileName: NormalizedPath) {
@ -1134,18 +1163,19 @@ namespace ts.server {
}
}
private reportConfigFileDiagnostics(configFileName: string, diagnostics: Diagnostic[], triggerFile: string) {
private reportConfigFileDiagnostics(configFileName: string, diagnostics: ReadonlyArray<Diagnostic>, triggerFile: string) {
if (!this.eventHandler) {
return;
}
this.eventHandler(<ConfigFileDiagEvent>{
const event: ConfigFileDiagEvent = {
eventName: ConfigFileDiagEvent,
data: { configFileName, diagnostics: diagnostics || [], triggerFile }
});
data: { configFileName, diagnostics: diagnostics || emptyArray, triggerFile }
};
this.eventHandler(event);
}
private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, configFileErrors: Diagnostic[], clientFileName?: string) {
private createAndAddConfiguredProject(configFileName: NormalizedPath, projectOptions: ProjectOptions, configFileErrors: ReadonlyArray<Diagnostic>, clientFileName?: string) {
const sizeLimitExceeded = this.exceededTotalSizeLimitForNonTsFiles(configFileName, projectOptions.compilerOptions, projectOptions.files, fileNamePropertyReader);
const project = new ConfiguredProject(
configFileName,
@ -1177,7 +1207,7 @@ namespace ts.server {
}
}
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: Diagnostic[]): void {
private addFilesToProjectAndUpdateGraph<T>(project: ConfiguredProject | ExternalProject, files: T[], propertyReader: FilePropertyReader<T>, clientFileName: string, typeAcquisition: TypeAcquisition, configFileErrors: ReadonlyArray<Diagnostic>): void {
let errors: Diagnostic[];
for (const f of files) {
const rootFilename = propertyReader.getFileName(f);
@ -1317,11 +1347,74 @@ namespace ts.server {
return configFileErrors;
}
createInferredProjectWithRootFileIfNecessary(root: ScriptInfo) {
const useExistingProject = this.useSingleInferredProject && this.inferredProjects.length;
const project = useExistingProject
? this.inferredProjects[0]
: new InferredProject(this, this.documentRegistry, this.compilerOptionsForInferredProjects);
private getOrCreateInferredProjectForProjectRootPathIfEnabled(root: ScriptInfo, projectRootPath: string | undefined): InferredProject | undefined {
if (!this.useInferredProjectPerProjectRoot) {
return undefined;
}
if (projectRootPath) {
// if we have an explicit project root path, find (or create) the matching inferred project.
for (const project of this.inferredProjects) {
if (project.projectRootPath === projectRootPath) {
return project;
}
}
return this.createInferredProject(/*isSingleInferredProject*/ false, projectRootPath);
}
// we don't have an explicit root path, so we should try to find an inferred project
// that more closely contains the file.
let bestMatch: InferredProject;
for (const project of this.inferredProjects) {
// ignore single inferred projects (handled elsewhere)
if (!project.projectRootPath) continue;
// ignore inferred projects that don't contain the root's path
if (!containsPath(project.projectRootPath, root.path, this.host.getCurrentDirectory(), !this.host.useCaseSensitiveFileNames)) continue;
// ignore inferred projects that are higher up in the project root.
// TODO(rbuckton): Should we add the file as a root to these as well?
if (bestMatch && bestMatch.projectRootPath.length > project.projectRootPath.length) continue;
bestMatch = project;
}
return bestMatch;
}
private getOrCreateSingleInferredProjectIfEnabled(): InferredProject | undefined {
if (!this.useSingleInferredProject) {
return undefined;
}
// If `useInferredProjectPerProjectRoot` is not enabled, then there will only be one
// inferred project for all files. If `useInferredProjectPerProjectRoot` is enabled
// then we want to put all files that are not opened with a `projectRootPath` into
// the same inferred project.
//
// To avoid the cost of searching through the array and to optimize for the case where
// `useInferredProjectPerProjectRoot` is not enabled, we will always put the inferred
// project for non-rooted files at the front of the array.
if (this.inferredProjects.length > 0 && this.inferredProjects[0].projectRootPath === undefined) {
return this.inferredProjects[0];
}
return this.createInferredProject(/*isSingleInferredProject*/ true);
}
private createInferredProject(isSingleInferredProject?: boolean, projectRootPath?: string): InferredProject {
const compilerOptions = projectRootPath && this.compilerOptionsForInferredProjectsPerProjectRoot.get(projectRootPath) || this.compilerOptionsForInferredProjects;
const project = new InferredProject(this, this.documentRegistry, compilerOptions, projectRootPath);
if (isSingleInferredProject) {
this.inferredProjects.unshift(project);
}
else {
this.inferredProjects.push(project);
}
return project;
}
createInferredProjectWithRootFileIfNecessary(root: ScriptInfo, projectRootPath?: string) {
const project = this.getOrCreateInferredProjectForProjectRootPathIfEnabled(root, projectRootPath) ||
this.getOrCreateSingleInferredProjectIfEnabled() ||
this.createInferredProject();
project.addRoot(root);
@ -1332,9 +1425,6 @@ namespace ts.server {
project.updateGraph();
if (!useExistingProject) {
this.inferredProjects.push(project);
}
return project;
}
@ -1490,7 +1580,7 @@ namespace ts.server {
openClientFileWithNormalizedPath(fileName: NormalizedPath, fileContent?: string, scriptKind?: ScriptKind, hasMixedContent?: boolean, projectRootPath?: NormalizedPath): OpenConfiguredProjectResult {
let configFileName: NormalizedPath;
let configFileErrors: Diagnostic[];
let configFileErrors: ReadonlyArray<Diagnostic>;
let project: ConfiguredProject | ExternalProject = this.findContainingExternalProject(fileName);
if (!project) {
@ -1508,7 +1598,7 @@ namespace ts.server {
// at this point if file is the part of some configured/external project then this project should be created
const info = this.getOrCreateScriptInfoForNormalizedPath(fileName, /*openedByClient*/ true, fileContent, scriptKind, hasMixedContent);
this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true);
this.assignScriptInfoToInferredProjectIfNecessary(info, /*addToListOfOpenFiles*/ true, projectRootPath);
// Delete the orphan files here because there might be orphan script infos (which are not part of project)
// when some file/s were closed which resulted in project removal.
// It was then postponed to cleanup these script infos so that they can be reused if

Просмотреть файл

@ -54,7 +54,7 @@ namespace ts.server {
/* @internal */
export interface ProjectFilesWithTSDiagnostics extends protocol.ProjectFiles {
projectErrors: Diagnostic[];
projectErrors: ReadonlyArray<Diagnostic>;
}
export class UnresolvedImportsMap {
@ -147,7 +147,7 @@ namespace ts.server {
private typingFiles: SortedReadonlyArray<string>;
protected projectErrors: Diagnostic[];
protected projectErrors: ReadonlyArray<Diagnostic>;
public typesVersion = 0;
@ -170,7 +170,8 @@ namespace ts.server {
log(`Loading ${moduleName} from ${initialDir} (resolved to ${resolvedPath})`);
const result = host.require(resolvedPath, moduleName);
if (result.error) {
log(`Failed to load module: ${JSON.stringify(result.error)}`);
const err = result.error.stack || result.error.message || JSON.stringify(result.error);
log(`Failed to load module '${moduleName}': ${err}`);
return undefined;
}
return result.module;
@ -362,7 +363,7 @@ namespace ts.server {
return map(this.program.getSourceFiles(), sourceFile => {
const scriptInfo = this.projectService.getScriptInfoForPath(sourceFile.path);
if (!scriptInfo) {
Debug.assert(false, `scriptInfo for a file '${sourceFile.fileName}' is missing.`);
Debug.fail(`scriptInfo for a file '${sourceFile.fileName}' is missing.`);
}
return scriptInfo;
});
@ -498,7 +499,7 @@ namespace ts.server {
this.projectStateVersion++;
}
private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: string[]) {
private extractUnresolvedImportsFromSourceFile(file: SourceFile, result: Push<string>) {
const cached = this.cachedUnresolvedImportsPerFile.get(file.path);
if (cached) {
// found cached result - use it and return
@ -558,7 +559,7 @@ namespace ts.server {
for (const sourceFile of this.program.getSourceFiles()) {
this.extractUnresolvedImportsFromSourceFile(sourceFile, result);
}
this.lastCachedUnresolvedImportsList = toSortedArray(result);
this.lastCachedUnresolvedImportsList = toDeduplicatedSortedArray(result);
}
unresolvedImports = this.lastCachedUnresolvedImportsList;
@ -840,6 +841,7 @@ namespace ts.server {
* the file and its imports/references are put into an InferredProject.
*/
export class InferredProject extends Project {
public readonly projectRootPath: string | undefined;
private static readonly newName = (() => {
let nextId = 1;
@ -879,7 +881,7 @@ namespace ts.server {
// Used to keep track of what directories are watched for this project
directoriesWatchedForTsconfig: string[] = [];
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions) {
constructor(projectService: ProjectService, documentRegistry: DocumentRegistry, compilerOptions: CompilerOptions, projectRootPath?: string) {
super(InferredProject.newName(),
ProjectKind.Inferred,
projectService,
@ -888,6 +890,7 @@ namespace ts.server {
/*languageServiceEnabled*/ true,
compilerOptions,
/*compileOnSaveEnabled*/ false);
this.projectRootPath = projectRootPath;
}
addRoot(info: ScriptInfo) {
@ -940,9 +943,9 @@ namespace ts.server {
export class ConfiguredProject extends Project {
private typeAcquisition: TypeAcquisition;
private projectFileWatcher: FileWatcher;
private directoryWatcher: FileWatcher;
private directoriesWatchedForWildcards: Map<FileWatcher>;
private typeRootsWatchers: FileWatcher[];
private directoryWatcher: FileWatcher | undefined;
private directoriesWatchedForWildcards: Map<FileWatcher> | undefined;
private typeRootsWatchers: FileWatcher[] | undefined;
readonly canonicalConfigFilePath: NormalizedPath;
private plugins: PluginModule[] = [];
@ -1048,7 +1051,7 @@ namespace ts.server {
return getDirectoryPath(this.getConfigFilePath());
}
setProjectErrors(projectErrors: Diagnostic[]) {
setProjectErrors(projectErrors: ReadonlyArray<Diagnostic>) {
this.projectErrors = projectErrors;
}
@ -1138,10 +1141,12 @@ namespace ts.server {
this.typeRootsWatchers = undefined;
}
this.directoriesWatchedForWildcards.forEach(watcher => {
watcher.close();
});
this.directoriesWatchedForWildcards = undefined;
if (this.directoriesWatchedForWildcards) {
this.directoriesWatchedForWildcards.forEach(watcher => {
watcher.close();
});
this.directoriesWatchedForWildcards = undefined;
}
this.stopWatchingDirectory();
}
@ -1196,7 +1201,7 @@ namespace ts.server {
return this.typeAcquisition;
}
setProjectErrors(projectErrors: Diagnostic[]) {
setProjectErrors(projectErrors: ReadonlyArray<Diagnostic>) {
this.projectErrors = projectErrors;
}

Просмотреть файл

@ -8,6 +8,7 @@ namespace ts.server.protocol {
/* @internal */
BraceFull = "brace-full",
BraceCompletion = "braceCompletion",
GetSpanOfEnclosingComment = "getSpanOfEnclosingComment",
Change = "change",
Close = "close",
Completions = "completions",
@ -241,6 +242,21 @@ namespace ts.server.protocol {
body?: TodoComment[];
}
/**
* A request to determine if the caret is inside a comment.
*/
export interface SpanOfEnclosingCommentRequest extends FileLocationRequest {
command: CommandTypes.GetSpanOfEnclosingComment;
arguments: SpanOfEnclosingCommentRequestArgs;
}
export interface SpanOfEnclosingCommentRequestArgs extends FileLocationRequestArgs {
/**
* Requires that the enclosing span be a multi-line comment, or else the request returns undefined.
*/
onlyMultiLine: boolean;
}
/**
* Request to obtain outlining spans in file.
*/
@ -914,7 +930,7 @@ namespace ts.server.protocol {
/**
* An array of span groups (one per file) that refer to the item to be renamed.
*/
locs: SpanGroup[];
locs: ReadonlyArray<SpanGroup>;
}
/**
@ -1304,6 +1320,13 @@ namespace ts.server.protocol {
* Compiler options to be used with inferred projects.
*/
options: ExternalProjectCompilerOptions;
/**
* Specifies the project root path used to scope compiler options.
* It is an error to provide this property if the server has not been started with
* `useInferredProjectPerProjectRoot` enabled.
*/
projectRootPath?: string;
}
/**
@ -2429,6 +2452,7 @@ namespace ts.server.protocol {
paths?: MapLike<string[]>;
plugins?: PluginImport[];
preserveConstEnums?: boolean;
preserveSymlinks?: boolean;
project?: string;
reactNamespace?: string;
removeComments?: boolean;
@ -2465,6 +2489,7 @@ namespace ts.server.protocol {
System = "System",
ES6 = "ES6",
ES2015 = "ES2015",
ESNext = "ESNext"
}
export const enum ModuleResolutionKind {
@ -2482,5 +2507,8 @@ namespace ts.server.protocol {
ES5 = "ES5",
ES6 = "ES6",
ES2015 = "ES2015",
ES2016 = "ES2016",
ES2017 = "ES2017",
ESNext = "ESNext"
}
}

Просмотреть файл

@ -16,7 +16,7 @@ namespace ts.server {
public getVersion() {
return this.svc
? `SVC-${this.svcVersion}-${this.svc.getSnapshot().version}`
? `SVC-${this.svcVersion}-${this.svc.getSnapshotVersion()}`
: `Text-${this.textVersion}`;
}
@ -62,22 +62,19 @@ namespace ts.server {
}
public getLineInfo(line: number): AbsolutePositionAndLineText {
return this.switchToScriptVersionCache().getSnapshot().index.lineNumberToInfo(line);
return this.switchToScriptVersionCache().getLineInfo(line);
}
/**
* @param line 0 based index
*/
lineToTextSpan(line: number) {
lineToTextSpan(line: number): TextSpan {
if (!this.svc) {
const lineMap = this.getLineMap();
const start = lineMap[line]; // -1 since line is 1-based
const end = line + 1 < lineMap.length ? lineMap[line + 1] : this.text.length;
return createTextSpanFromBounds(start, end);
}
const index = this.svc.getSnapshot().index;
const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1);
const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition;
return createTextSpan(absolutePosition, len);
return this.svc.lineToTextSpan(line);
}
/**
@ -90,7 +87,7 @@ namespace ts.server {
}
// TODO: assert this offset is actually on the line
return this.svc.getSnapshot().index.absolutePositionOfStartOfLine(line) + (offset - 1);
return this.svc.lineOffsetToPosition(line, offset);
}
positionToLineOffset(position: number): protocol.Location {
@ -98,7 +95,7 @@ namespace ts.server {
const { line, character } = computeLineAndCharacterOfPosition(this.getLineMap(), position);
return { line: line + 1, offset: character + 1 };
}
return this.svc.getSnapshot().index.positionToLineOffset(position);
return this.svc.positionToLineOffset(position);
}
private getFileText(tempFileName?: string) {

Просмотреть файл

@ -5,7 +5,7 @@
namespace ts.server {
const lineCollectionCapacity = 4;
export interface LineCollection {
interface LineCollection {
charCount(): number;
lineCount(): number;
isLeaf(): this is LineLeaf;
@ -17,7 +17,7 @@ namespace ts.server {
lineText: string | undefined;
}
export enum CharRangeSection {
const enum CharRangeSection {
PreStart,
Start,
Entire,
@ -26,7 +26,7 @@ namespace ts.server {
PostEnd
}
export interface ILineIndexWalker {
interface ILineIndexWalker {
goSubtree: boolean;
done: boolean;
leaf(relativeStart: number, relativeLength: number, lineCollection: LineLeaf): void;
@ -243,7 +243,7 @@ namespace ts.server {
}
// text change information
export class TextChange {
class TextChange {
constructor(public pos: number, public deleteLen: number, public insertedText?: string) {
}
@ -285,17 +285,6 @@ namespace ts.server {
}
}
latest() {
return this.versions[this.currentVersionToIndex()];
}
latestVersion() {
if (this.changes.length > 0) {
this.getSnapshot();
}
return this.currentVersion;
}
// reload whole script, leaving no change history behind reload
reload(script: string) {
this.currentVersion++;
@ -314,7 +303,9 @@ namespace ts.server {
this.minVersion = this.currentVersion;
}
getSnapshot() {
getSnapshot(): IScriptSnapshot { return this._getSnapshot(); }
private _getSnapshot(): LineIndexSnapshot {
let snap = this.versions[this.currentVersionToIndex()];
if (this.changes.length > 0) {
let snapIndex = snap.index;
@ -334,6 +325,29 @@ namespace ts.server {
return snap;
}
getSnapshotVersion(): number {
return this._getSnapshot().version;
}
getLineInfo(line: number): AbsolutePositionAndLineText {
return this._getSnapshot().index.lineNumberToInfo(line);
}
lineOffsetToPosition(line: number, column: number): number {
return this._getSnapshot().index.absolutePositionOfStartOfLine(line) + (column - 1);
}
positionToLineOffset(position: number): protocol.Location {
return this._getSnapshot().index.positionToLineOffset(position);
}
lineToTextSpan(line: number): TextSpan {
const index = this._getSnapshot().index;
const { lineText, absolutePosition } = index.lineNumberToInfo(line + 1);
const len = lineText !== undefined ? lineText.length : index.absolutePositionOfStartOfLine(line + 2) - absolutePosition;
return createTextSpan(absolutePosition, len);
}
getTextChangesBetweenVersions(oldVersion: number, newVersion: number) {
if (oldVersion < newVersion) {
if (oldVersion >= this.minVersion) {
@ -365,7 +379,7 @@ namespace ts.server {
}
}
export class LineIndexSnapshot implements IScriptSnapshot {
class LineIndexSnapshot implements IScriptSnapshot {
constructor(readonly version: number, readonly cache: ScriptVersionCache, readonly index: LineIndex, readonly changesSincePreviousVersion: ReadonlyArray<TextChange> = emptyArray) {
}
@ -389,6 +403,7 @@ namespace ts.server {
}
}
/* @internal */
export class LineIndex {
root: LineNode;
// set this to true to check each edit for accuracy
@ -561,7 +576,7 @@ namespace ts.server {
}
}
export class LineNode implements LineCollection {
class LineNode implements LineCollection {
totalChars = 0;
totalLines = 0;
@ -660,45 +675,29 @@ namespace ts.server {
// Input position is relative to the start of this node.
// Output line number is absolute.
charOffsetToLineInfo(lineNumberAccumulator: number, relativePosition: number): { oneBasedLine: number, zeroBasedColumn: number, lineText: string | undefined } {
const childInfo = this.childFromCharOffset(lineNumberAccumulator, relativePosition);
if (!childInfo.child) {
return {
oneBasedLine: lineNumberAccumulator,
zeroBasedColumn: relativePosition,
lineText: undefined,
};
if (this.children.length === 0) {
// Root node might have no children if this is an empty document.
return { oneBasedLine: lineNumberAccumulator, zeroBasedColumn: relativePosition, lineText: undefined };
}
else if (childInfo.childIndex < this.children.length) {
if (childInfo.child.isLeaf()) {
return {
oneBasedLine: childInfo.lineNumberAccumulator,
zeroBasedColumn: childInfo.relativePosition,
lineText: childInfo.child.text,
};
for (const child of this.children) {
if (child.charCount() > relativePosition) {
if (child.isLeaf()) {
return { oneBasedLine: lineNumberAccumulator, zeroBasedColumn: relativePosition, lineText: child.text };
}
else {
return (<LineNode>child).charOffsetToLineInfo(lineNumberAccumulator, relativePosition);
}
}
else {
const lineNode = <LineNode>(childInfo.child);
return lineNode.charOffsetToLineInfo(childInfo.lineNumberAccumulator, childInfo.relativePosition);
relativePosition -= child.charCount();
lineNumberAccumulator += child.lineCount();
}
}
else {
const lineInfo = this.lineNumberToInfo(this.lineCount(), 0);
return { oneBasedLine: this.lineCount(), zeroBasedColumn: lineInfo.leaf.charCount(), lineText: undefined };
}
}
lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } {
const childInfo = this.childFromLineNumber(relativeOneBasedLine, positionAccumulator);
if (!childInfo.child) {
return { position: positionAccumulator, leaf: undefined };
}
else if (childInfo.child.isLeaf()) {
return { position: childInfo.positionAccumulator, leaf: childInfo.child };
}
else {
const lineNode = <LineNode>(childInfo.child);
return lineNode.lineNumberToInfo(childInfo.relativeOneBasedLine, childInfo.positionAccumulator);
}
// Skipped all children
const { leaf } = this.lineNumberToInfo(this.lineCount(), 0);
return { oneBasedLine: this.lineCount(), zeroBasedColumn: leaf.charCount(), lineText: undefined };
}
/**
@ -706,39 +705,19 @@ namespace ts.server {
* Output line number is relative to the child.
* positionAccumulator will be an absolute position once relativeLineNumber reaches 0.
*/
private childFromLineNumber(relativeOneBasedLine: number, positionAccumulator: number): { child: LineCollection, relativeOneBasedLine: number, positionAccumulator: number } {
let child: LineCollection;
let i: number;
for (i = 0; i < this.children.length; i++) {
child = this.children[i];
lineNumberToInfo(relativeOneBasedLine: number, positionAccumulator: number): { position: number, leaf: LineLeaf | undefined } {
for (const child of this.children) {
const childLineCount = child.lineCount();
if (childLineCount >= relativeOneBasedLine) {
break;
return child.isLeaf() ? { position: positionAccumulator, leaf: child } : (<LineNode>child).lineNumberToInfo(relativeOneBasedLine, positionAccumulator);
}
else {
relativeOneBasedLine -= childLineCount;
positionAccumulator += child.charCount();
}
}
return { child, relativeOneBasedLine, positionAccumulator };
}
private childFromCharOffset(lineNumberAccumulator: number, relativePosition: number
): { child: LineCollection, childIndex: number, relativePosition: number, lineNumberAccumulator: number } {
let child: LineCollection;
let i: number;
let len: number;
for (i = 0, len = this.children.length; i < len; i++) {
child = this.children[i];
if (child.charCount() > relativePosition) {
break;
}
else {
relativePosition -= child.charCount();
lineNumberAccumulator += child.lineCount();
}
}
return { child, childIndex: i, relativePosition, lineNumberAccumulator };
return { position: positionAccumulator, leaf: undefined };
}
private splitAfter(childIndex: number) {
@ -844,7 +823,7 @@ namespace ts.server {
}
}
export class LineLeaf implements LineCollection {
class LineLeaf implements LineCollection {
constructor(public text: string) {
}

Просмотреть файл

@ -9,6 +9,7 @@ namespace ts.server {
canUseEvents: boolean;
installerEventPort: number;
useSingleInferredProject: boolean;
useInferredProjectPerProjectRoot: boolean;
disableAutomaticTypingAcquisition: boolean;
globalTypingsCacheLocation: string;
logger: Logger;
@ -16,8 +17,8 @@ namespace ts.server {
typesMapLocation: string | undefined;
npmLocation: string | undefined;
telemetryEnabled: boolean;
globalPlugins: string[];
pluginProbeLocations: string[];
globalPlugins: ReadonlyArray<string>;
pluginProbeLocations: ReadonlyArray<string>;
allowLocalPluginLoads: boolean;
}
@ -117,8 +118,6 @@ namespace ts.server {
birthtime: Date;
}
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} };
const readline: {
createInterface(options: ReadLineOptions): NodeJS.EventEmitter;
} = require("readline");
@ -180,6 +179,10 @@ namespace ts.server {
this.msg(s, Msg.Info);
}
err(s: string) {
this.msg(s, Msg.Err);
}
startGroup() {
this.inGroup = true;
this.firstInGroup = true;
@ -187,8 +190,6 @@ namespace ts.server {
endGroup() {
this.inGroup = false;
this.seq++;
this.firstInGroup = true;
}
loggingEnabled() {
@ -200,25 +201,31 @@ namespace ts.server {
}
msg(s: string, type: Msg.Types = Msg.Err) {
if (this.fd >= 0 || this.traceToConsole) {
s = `[${nowString()}] ${s}\n`;
if (!this.canWrite) return;
s = `[${nowString()}] ${s}\n`;
if (!this.inGroup || this.firstInGroup) {
const prefix = Logger.padStringRight(type + " " + this.seq.toString(), " ");
if (this.firstInGroup) {
s = prefix + s;
this.firstInGroup = false;
}
if (!this.inGroup) {
this.seq++;
this.firstInGroup = true;
}
if (this.fd >= 0) {
const buf = new Buffer(s);
// tslint:disable-next-line no-null-keyword
fs.writeSync(this.fd, buf, 0, buf.length, /*position*/ null);
}
if (this.traceToConsole) {
console.warn(s);
}
s = prefix + s;
}
this.write(s);
if (!this.inGroup) {
this.seq++;
}
}
private get canWrite() {
return this.fd >= 0 || this.traceToConsole;
}
private write(s: string) {
if (this.fd >= 0) {
const buf = new Buffer(s);
// tslint:disable-next-line no-null-keyword
fs.writeSync(this.fd, buf, 0, buf.length, /*position*/ null);
}
if (this.traceToConsole) {
console.warn(s);
}
}
}
@ -415,6 +422,7 @@ namespace ts.server {
host,
cancellationToken,
useSingleInferredProject,
useInferredProjectPerProjectRoot,
typingsInstaller: typingsInstaller || nullTypingsInstaller,
byteLength: Buffer.byteLength,
hrtime: process.hrtime,
@ -762,15 +770,26 @@ namespace ts.server {
validateLocaleAndSetLanguage(localeStr, sys);
}
setStackTraceLimit();
const typingSafeListLocation = findArgument(Arguments.TypingSafeListLocation);
const typesMapLocation = findArgument(Arguments.TypesMapLocation) || combinePaths(sys.getExecutingFilePath(), "../typesMap.json");
const npmLocation = findArgument(Arguments.NpmLocation);
const globalPlugins = (findArgument("--globalPlugins") || "").split(",");
const pluginProbeLocations = (findArgument("--pluginProbeLocations") || "").split(",");
function parseStringArray(argName: string): ReadonlyArray<string> {
const arg = findArgument(argName);
if (arg === undefined) {
return emptyArray;
}
return arg.split(",").filter(name => name !== "");
}
const globalPlugins = parseStringArray("--globalPlugins");
const pluginProbeLocations = parseStringArray("--pluginProbeLocations");
const allowLocalPluginLoads = hasArgument("--allowLocalPluginLoads");
const useSingleInferredProject = hasArgument("--useSingleInferredProject");
const useInferredProjectPerProjectRoot = hasArgument("--useInferredProjectPerProjectRoot");
const disableAutomaticTypingAcquisition = hasArgument("--disableAutomaticTypingAcquisition");
const telemetryEnabled = hasArgument(Arguments.EnableTelemetry);
@ -780,6 +799,7 @@ namespace ts.server {
installerEventPort: eventPort,
canUseEvents: eventPort === undefined,
useSingleInferredProject,
useInferredProjectPerProjectRoot,
disableAutomaticTypingAcquisition,
globalTypingsCacheLocation: getGlobalTypingsCacheLocation(),
typingSafeListLocation,

Просмотреть файл

@ -163,26 +163,22 @@ namespace ts.server {
* Scheduling is done via instance of NextStep. If on current step subsequent step was not scheduled - operation is assumed to be completed.
*/
class MultistepOperation implements NextStep {
private requestId: number;
private requestId: number | undefined;
private timerHandle: any;
private immediateId: any;
private completed = true;
private immediateId: number | undefined;
constructor(private readonly operationHost: MultistepOperationHost) {}
public startNew(action: (next: NextStep) => void) {
this.complete();
this.requestId = this.operationHost.getCurrentRequestId();
this.completed = false;
this.executeAction(action);
}
private complete() {
if (!this.completed) {
if (this.requestId) {
this.operationHost.sendRequestCompletedEvent(this.requestId);
}
this.completed = true;
if (this.requestId !== undefined) {
this.operationHost.sendRequestCompletedEvent(this.requestId);
this.requestId = undefined;
}
this.setTimerHandle(undefined);
this.setImmediateId(undefined);
@ -251,6 +247,7 @@ namespace ts.server {
host: ServerHost;
cancellationToken: ServerCancellationToken;
useSingleInferredProject: boolean;
useInferredProjectPerProjectRoot: boolean;
typingsInstaller: ITypingsInstaller;
byteLength: (buf: string, encoding?: string) => number;
hrtime: (start?: number[]) => number[];
@ -259,8 +256,8 @@ namespace ts.server {
eventHandler?: ProjectServiceEventHandler;
throttleWaitMilliseconds?: number;
globalPlugins?: string[];
pluginProbeLocations?: string[];
globalPlugins?: ReadonlyArray<string>;
pluginProbeLocations?: ReadonlyArray<string>;
allowLocalPluginLoads?: boolean;
}
@ -311,6 +308,7 @@ namespace ts.server {
logger: this.logger,
cancellationToken: this.cancellationToken,
useSingleInferredProject: opts.useSingleInferredProject,
useInferredProjectPerProjectRoot: opts.useInferredProjectPerProjectRoot,
typingsInstaller: this.typingsInstaller,
throttleWaitMilliseconds,
eventHandler: this.eventHandler,
@ -337,7 +335,7 @@ namespace ts.server {
case ContextEvent:
const { project, fileName } = event.data;
this.projectService.logger.info(`got context event, updating diagnostics for ${fileName}`);
this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], this.changeSeq, (n) => n === this.changeSeq, 100));
this.errorCheck.startNew(next => this.updateErrorCheck(next, [{ fileName, project }], 100));
break;
case ConfigFileDiagEvent:
const { triggerFile, configFileName, diagnostics } = event.data;
@ -383,7 +381,7 @@ namespace ts.server {
this.host.write(formatMessage(msg, this.logger, this.byteLength, this.host.newLine));
}
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: Diagnostic[]) {
public configFileDiagnosticEvent(triggerFile: string, configFile: string, diagnostics: ReadonlyArray<Diagnostic>) {
const bakedDiags = map(diagnostics, diagnostic => formatConfigFileDiag(diagnostic, /*includeFileName*/ true));
const ev: protocol.ConfigFileDiagnosticEvent = {
seq: 0,
@ -453,22 +451,23 @@ namespace ts.server {
}
}
private updateProjectStructure(seq: number, matchSeq: (seq: number) => boolean, ms = 1500) {
private updateProjectStructure() {
const ms = 1500;
const seq = this.changeSeq;
this.host.setTimeout(() => {
if (matchSeq(seq)) {
if (this.changeSeq === seq) {
this.projectService.refreshInferredProjects();
}
}, ms);
}
private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], seq: number, matchSeq: (seq: number) => boolean, ms = 1500, followMs = 200, requireOpen = true) {
if (followMs > ms) {
followMs = ms;
}
private updateErrorCheck(next: NextStep, checkList: PendingErrorCheck[], ms: number, requireOpen = true) {
const seq = this.changeSeq;
const followMs = Math.min(ms, 200);
let index = 0;
const checkOne = () => {
if (matchSeq(seq)) {
if (this.changeSeq === seq) {
const checkSpec = checkList[index];
index++;
if (checkSpec.project.containsFile(checkSpec.fileName, requireOpen)) {
@ -483,7 +482,7 @@ namespace ts.server {
}
};
if ((checkList.length > index) && (matchSeq(seq))) {
if (checkList.length > index && this.changeSeq === seq) {
next.delay(ms, checkOne);
}
}
@ -542,8 +541,8 @@ namespace ts.server {
);
}
private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: Diagnostic[]) {
return diagnostics.map(d => <protocol.DiagnosticWithLinePosition>{
private convertToDiagnosticsWithLinePositionFromDiagnosticFile(diagnostics: ReadonlyArray<Diagnostic>): protocol.DiagnosticWithLinePosition[] {
return diagnostics.map<protocol.DiagnosticWithLinePosition>(d => ({
message: flattenDiagnosticMessageText(d.messageText, this.host.newLine),
start: d.start,
length: d.length,
@ -551,7 +550,7 @@ namespace ts.server {
code: d.code,
startLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start)),
endLocation: d.file && convertToLocation(getLineAndCharacterOfPosition(d.file, d.start + d.length))
});
}));
}
private getCompilerOptionsDiagnostics(args: protocol.CompilerOptionsDiagnosticsRequestArgs) {
@ -568,7 +567,7 @@ namespace ts.server {
);
}
private convertToDiagnosticsWithLinePosition(diagnostics: Diagnostic[], scriptInfo: ScriptInfo) {
private convertToDiagnosticsWithLinePosition(diagnostics: ReadonlyArray<Diagnostic>, scriptInfo: ScriptInfo): protocol.DiagnosticWithLinePosition[] {
return diagnostics.map(d => <protocol.DiagnosticWithLinePosition>{
message: flattenDiagnosticMessageText(d.messageText, this.host.newLine),
start: d.start,
@ -581,10 +580,12 @@ namespace ts.server {
});
}
private getDiagnosticsWorker(args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => Diagnostic[], includeLinePosition: boolean) {
private getDiagnosticsWorker(
args: protocol.FileRequestArgs, isSemantic: boolean, selector: (project: Project, file: string) => ReadonlyArray<Diagnostic>, includeLinePosition: boolean
): ReadonlyArray<protocol.DiagnosticWithLinePosition> | ReadonlyArray<protocol.Diagnostic> {
const { project, file } = this.getFileAndProject(args);
if (isSemantic && isDeclarationFileInJSOnlyNonConfiguredProject(project, file)) {
return [];
return emptyArray;
}
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const diagnostics = selector(project, file);
@ -593,14 +594,14 @@ namespace ts.server {
: diagnostics.map(d => formatDiag(file, project, d));
}
private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | DefinitionInfo[] {
private getDefinition(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileSpan> | ReadonlyArray<DefinitionInfo> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
const definitions = project.getLanguageService().getDefinitionAtPosition(file, position);
if (!definitions) {
return undefined;
return emptyArray;
}
if (simplifiedResult) {
@ -618,14 +619,14 @@ namespace ts.server {
}
}
private getTypeDefinition(args: protocol.FileLocationRequestArgs): protocol.FileSpan[] {
private getTypeDefinition(args: protocol.FileLocationRequestArgs): ReadonlyArray<protocol.FileSpan> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
const definitions = project.getLanguageService().getTypeDefinitionAtPosition(file, position);
if (!definitions) {
return undefined;
return emptyArray;
}
return definitions.map(def => {
@ -638,12 +639,12 @@ namespace ts.server {
});
}
private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.FileSpan[] | ImplementationLocation[] {
private getImplementation(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.FileSpan> | ReadonlyArray<ImplementationLocation> {
const { file, project } = this.getFileAndProject(args);
const position = this.getPosition(args, project.getScriptInfoForNormalizedPath(file));
const implementations = project.getLanguageService().getImplementationAtPosition(file, position);
if (!implementations) {
return [];
return emptyArray;
}
if (simplifiedResult) {
return implementations.map(({ fileName, textSpan }) => {
@ -660,7 +661,7 @@ namespace ts.server {
}
}
private getOccurrences(args: protocol.FileLocationRequestArgs): protocol.OccurrencesResponseItem[] {
private getOccurrences(args: protocol.FileLocationRequestArgs): ReadonlyArray<protocol.OccurrencesResponseItem> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
@ -668,7 +669,7 @@ namespace ts.server {
const occurrences = project.getLanguageService().getOccurrencesAtPosition(file, position);
if (!occurrences) {
return undefined;
return emptyArray;
}
return occurrences.map(occurrence => {
@ -690,17 +691,17 @@ namespace ts.server {
});
}
private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] {
private getSyntacticDiagnosticsSync(args: protocol.SyntacticDiagnosticsSyncRequestArgs): ReadonlyArray<protocol.Diagnostic> | ReadonlyArray<protocol.DiagnosticWithLinePosition> {
const { configFile } = this.getConfigFileAndProject(args);
if (configFile) {
// all the config file errors are reported as part of semantic check so nothing to report here
return [];
return emptyArray;
}
return this.getDiagnosticsWorker(args, /*isSemantic*/ false, (project, file) => project.getLanguageService().getSyntacticDiagnostics(file), args.includeLinePosition);
}
private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): protocol.Diagnostic[] | protocol.DiagnosticWithLinePosition[] {
private getSemanticDiagnosticsSync(args: protocol.SemanticDiagnosticsSyncRequestArgs): ReadonlyArray<protocol.Diagnostic> | ReadonlyArray<protocol.DiagnosticWithLinePosition> {
const { configFile, project } = this.getConfigFileAndProject(args);
if (configFile) {
return this.getConfigFileDiagnostics(configFile, project, args.includeLinePosition);
@ -708,14 +709,14 @@ namespace ts.server {
return this.getDiagnosticsWorker(args, /*isSemantic*/ true, (project, file) => project.getLanguageService().getSemanticDiagnostics(file), args.includeLinePosition);
}
private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): protocol.DocumentHighlightsItem[] | DocumentHighlights[] {
private getDocumentHighlights(args: protocol.DocumentHighlightsRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.DocumentHighlightsItem> | ReadonlyArray<DocumentHighlights> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
const documentHighlights = project.getLanguageService().getDocumentHighlights(file, position, args.filesToSearch);
if (!documentHighlights) {
return undefined;
return emptyArray;
}
if (simplifiedResult) {
@ -744,7 +745,7 @@ namespace ts.server {
}
private setCompilerOptionsForInferredProjects(args: protocol.SetCompilerOptionsForInferredProjectsArgs): void {
this.projectService.setCompilerOptionsForInferredProjects(args.options);
this.projectService.setCompilerOptionsForInferredProjects(args.options, args.projectRootPath);
}
private getProjectInfo(args: protocol.ProjectInfoRequestArgs): protocol.ProjectInfo {
@ -799,7 +800,7 @@ namespace ts.server {
return info.getDefaultProject();
}
private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | RenameLocation[] {
private getRenameLocations(args: protocol.RenameRequestArgs, simplifiedResult: boolean): protocol.RenameResponseBody | ReadonlyArray<RenameLocation> {
const file = toNormalizedPath(args.file);
const info = this.projectService.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, info);
@ -816,7 +817,7 @@ namespace ts.server {
if (!renameInfo.canRename) {
return {
info: renameInfo,
locs: []
locs: emptyArray
};
}
@ -825,12 +826,12 @@ namespace ts.server {
(project: Project) => {
const renameLocations = project.getLanguageService().findRenameLocations(file, position, args.findInStrings, args.findInComments);
if (!renameLocations) {
return [];
return emptyArray;
}
return renameLocations.map(location => {
const locationScriptInfo = project.getScriptInfo(location.fileName);
return <protocol.FileSpan>{
return {
file: location.fileName,
start: locationScriptInfo.positionToLineOffset(location.textSpan.start),
end: locationScriptInfo.positionToLineOffset(textSpanEnd(location.textSpan)),
@ -902,7 +903,7 @@ namespace ts.server {
}
}
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | ReferencedSymbol[] {
private getReferences(args: protocol.FileLocationRequestArgs, simplifiedResult: boolean): protocol.ReferencesResponseBody | undefined | ReadonlyArray<ReferencedSymbol> {
const file = toNormalizedPath(args.file);
const projects = this.getProjects(args);
@ -924,7 +925,7 @@ namespace ts.server {
(project: Project) => {
const references = project.getLanguageService().getReferencesAtPosition(file, position);
if (!references) {
return [];
return emptyArray;
}
return references.map(ref => {
@ -981,7 +982,7 @@ namespace ts.server {
if (this.eventHandler) {
this.eventHandler({
eventName: "configFileDiag",
data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || [] }
data: { triggerFile: fileName, configFileName, diagnostics: configFileErrors || emptyArray }
});
}
}
@ -1024,6 +1025,14 @@ namespace ts.server {
return project.getLanguageService(/*ensureSynchronized*/ false).getDocCommentTemplateAtPosition(file, position);
}
private getSpanOfEnclosingComment(args: protocol.SpanOfEnclosingCommentRequestArgs) {
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const onlyMultiLine = args.onlyMultiLine;
const position = this.getPosition(args, scriptInfo);
return project.getLanguageService(/*ensureSynchronized*/ false).getSpanOfEnclosingComment(file, position, onlyMultiLine);
}
private getIndentation(args: protocol.IndentationRequestArgs) {
const { file, project } = this.getFileAndProjectWithoutRefreshingInferredProjects(args);
const position = this.getPosition(args, project.getScriptInfoForNormalizedPath(file));
@ -1166,7 +1175,7 @@ namespace ts.server {
});
}
private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): protocol.CompletionEntry[] | CompletionInfo {
private getCompletions(args: protocol.CompletionsRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.CompletionEntry> | CompletionInfo | undefined {
const prefix = args.prefix || "";
const { file, project } = this.getFileAndProject(args);
@ -1174,11 +1183,8 @@ namespace ts.server {
const position = this.getPosition(args, scriptInfo);
const completions = project.getLanguageService().getCompletionsAtPosition(file, position);
if (!completions) {
return undefined;
}
if (simplifiedResult) {
return mapDefined(completions.entries, entry => {
return mapDefined(completions && completions.entries, entry => {
if (completions.isMemberCompletion || (entry.name.toLowerCase().indexOf(prefix.toLowerCase()) === 0)) {
const { name, kind, kindModifiers, sortText, replacementSpan } = entry;
const convertedSpan = replacementSpan ? this.decorateSpan(replacementSpan, scriptInfo) : undefined;
@ -1191,7 +1197,7 @@ namespace ts.server {
}
}
private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): protocol.CompletionEntryDetails[] {
private getCompletionEntryDetails(args: protocol.CompletionDetailsRequestArgs): ReadonlyArray<protocol.CompletionEntryDetails> {
const { file, project } = this.getFileAndProject(args);
const scriptInfo = project.getScriptInfoForNormalizedPath(file);
const position = this.getPosition(args, scriptInfo);
@ -1200,12 +1206,12 @@ namespace ts.server {
project.getLanguageService().getCompletionEntryDetails(file, position, entryName));
}
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): protocol.CompileOnSaveAffectedFileListSingleProject[] {
private getCompileOnSaveAffectedFileList(args: protocol.FileRequestArgs): ReadonlyArray<protocol.CompileOnSaveAffectedFileListSingleProject> {
const info = this.projectService.getScriptInfo(args.file);
const result: protocol.CompileOnSaveAffectedFileListSingleProject[] = [];
if (!info) {
return [];
return emptyArray;
}
// if specified a project, we only return affected file list in this project
@ -1262,14 +1268,14 @@ namespace ts.server {
}
private getDiagnostics(next: NextStep, delay: number, fileNames: string[]): void {
const checkList = mapDefined(fileNames, uncheckedFileName => {
const checkList = mapDefined<string, PendingErrorCheck>(fileNames, uncheckedFileName => {
const fileName = toNormalizedPath(uncheckedFileName);
const project = this.projectService.getDefaultProjectForFile(fileName, /*refreshInferredProjects*/ true);
return project && { fileName, project };
});
if (checkList.length > 0) {
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay);
this.updateErrorCheck(next, checkList, delay);
}
}
@ -1283,7 +1289,7 @@ namespace ts.server {
scriptInfo.editContent(start, end, args.insertString);
this.changeSeq++;
}
this.updateProjectStructure(this.changeSeq, n => n === this.changeSeq);
this.updateProjectStructure();
}
}
@ -1363,7 +1369,7 @@ namespace ts.server {
: tree;
}
private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): protocol.NavtoItem[] | NavigateToItem[] {
private getNavigateToItems(args: protocol.NavtoRequestArgs, simplifiedResult: boolean): ReadonlyArray<protocol.NavtoItem> | ReadonlyArray<NavigateToItem> {
const projects = this.getProjects(args);
const fileName = args.currentFileOnly ? args.file && normalizeSlashes(args.file) : undefined;
@ -1373,7 +1379,7 @@ namespace ts.server {
project => {
const navItems = project.getLanguageService().getNavigateToItems(args.searchValue, args.maxResultCount, fileName, /*excludeDts*/ project.isNonTsProject());
if (!navItems) {
return [];
return emptyArray;
}
return navItems.map((navItem) => {
@ -1638,7 +1644,7 @@ namespace ts.server {
const checkList = fileNamesInProject.map(fileName => ({ fileName, project }));
// Project level error analysis runs on background files too, therefore
// doesn't require the file to be opened
this.updateErrorCheck(next, checkList, this.changeSeq, (n) => n === this.changeSeq, delay, 200, /*requireOpen*/ false);
this.updateErrorCheck(next, checkList, delay, /*requireOpen*/ false);
}
}
@ -1767,6 +1773,9 @@ namespace ts.server {
[CommandNames.DocCommentTemplate]: (request: protocol.DocCommentTemplateRequest) => {
return this.requiredResponse(this.getDocCommentTemplate(request.arguments));
},
[CommandNames.GetSpanOfEnclosingComment]: (request: protocol.SpanOfEnclosingCommentRequest) => {
return this.requiredResponse(this.getSpanOfEnclosingComment(request.arguments));
},
[CommandNames.Format]: (request: protocol.FormatRequest) => {
return this.requiredResponse(this.getFormattingEditsForRange(request.arguments));
},

Просмотреть файл

@ -9,7 +9,7 @@ declare namespace ts.server {
data: any;
}
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: {} };
type RequireResult = { module: {}, error: undefined } | { module: undefined, error: { stack?: string, message?: string } };
export interface ServerHost extends System {
setTimeout(callback: (...args: any[]) => void, ms: number, ...args: any[]): any;
clearTimeout(timeoutId: any): void;

Просмотреть файл

@ -103,7 +103,7 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`Updating ${TypesRegistryPackageName} npm package...`);
}
this.execSync(`${this.npmPath} install ${TypesRegistryPackageName}`, { cwd: globalTypingsCacheLocation, stdio: "ignore" });
this.execSync(`${this.npmPath} install --ignore-scripts ${TypesRegistryPackageName}`, { cwd: globalTypingsCacheLocation, stdio: "ignore" });
if (this.log.isEnabled()) {
this.log.writeLine(`Updated ${TypesRegistryPackageName} npm package`);
}
@ -153,7 +153,7 @@ namespace ts.server.typingsInstaller {
if (this.log.isEnabled()) {
this.log.writeLine(`#${requestId} with arguments'${JSON.stringify(args)}'.`);
}
const command = `${this.npmPath} install ${args.join(" ")} --save-dev --user-agent="typesInstaller/${version}"`;
const command = `${this.npmPath} install --ignore-scripts ${args.join(" ")} --save-dev --user-agent="typesInstaller/${version}"`;
const start = Date.now();
let stdout: Buffer;
let stderr: Buffer;

Просмотреть файл

@ -371,14 +371,15 @@ namespace ts.server.typingsInstaller {
this.sendResponse(this.createSetTypings(req, currentlyCachedTypings.concat(installedTypingFiles)));
}
finally {
this.sendResponse(<EndInstallTypes>{
const response: EndInstallTypes = {
kind: EventEndInstallTypes,
eventId: requestId,
projectName: req.projectName,
packagesToInstall: scopedTypings,
installSuccess: ok,
typingsInstallerVersion: ts.version // qualified explicitly to prevent occasional shadowing
});
};
this.sendResponse(response);
}
});
}

Просмотреть файл

@ -126,9 +126,7 @@ namespace ts.server {
}
export function createNormalizedPathMap<T>(): NormalizedPathMap<T> {
/* tslint:disable:no-null-keyword */
const map = createMap<T>();
/* tslint:enable:no-null-keyword */
return {
get(path) {
return map.get(path);
@ -176,44 +174,6 @@ namespace ts.server {
return [] as SortedArray<T>;
}
export function toSortedArray(arr: string[]): SortedArray<string>;
export function toSortedArray<T>(arr: T[], comparer: Comparer<T>): SortedArray<T>;
export function toSortedArray<T>(arr: T[], comparer?: Comparer<T>): SortedArray<T> {
arr.sort(comparer);
return arr as SortedArray<T>;
}
export function enumerateInsertsAndDeletes<T>(newItems: SortedReadonlyArray<T>, oldItems: SortedReadonlyArray<T>, inserted: (newItem: T) => void, deleted: (oldItem: T) => void, compare?: Comparer<T>) {
compare = compare || compareValues;
let newIndex = 0;
let oldIndex = 0;
const newLen = newItems.length;
const oldLen = oldItems.length;
while (newIndex < newLen && oldIndex < oldLen) {
const newItem = newItems[newIndex];
const oldItem = oldItems[oldIndex];
const compareResult = compare(newItem, oldItem);
if (compareResult === Comparison.LessThan) {
inserted(newItem);
newIndex++;
}
else if (compareResult === Comparison.GreaterThan) {
deleted(oldItem);
oldIndex++;
}
else {
newIndex++;
oldIndex++;
}
}
while (newIndex < newLen) {
inserted(newItems[newIndex++]);
}
while (oldIndex < oldLen) {
deleted(oldItems[oldIndex++]);
}
}
export class ThrottledOperations {
private pendingTimeouts: Map<any> = createMap<any>();
constructor(private readonly host: ServerHost) {
@ -261,7 +221,10 @@ namespace ts.server {
}
}
}
}
/* @internal */
namespace ts.server {
export function insertSorted<T>(array: SortedArray<T>, insert: T, compare: Comparer<T>): void {
if (array.length === 0) {
array.push(insert);
@ -289,4 +252,51 @@ namespace ts.server {
array.splice(removeIndex, 1);
}
}
export function toSortedArray(arr: string[]): SortedArray<string>;
export function toSortedArray<T>(arr: T[], comparer: Comparer<T>): SortedArray<T>;
export function toSortedArray<T>(arr: T[], comparer?: Comparer<T>): SortedArray<T> {
arr.sort(comparer);
return arr as SortedArray<T>;
}
export function toDeduplicatedSortedArray(arr: string[]): SortedArray<string> {
arr.sort();
filterMutate(arr, isNonDuplicateInSortedArray);
return arr as SortedArray<string>;
}
function isNonDuplicateInSortedArray<T>(value: T, index: number, array: T[]) {
return index === 0 || value !== array[index - 1];
}
export function enumerateInsertsAndDeletes<T>(newItems: SortedReadonlyArray<T>, oldItems: SortedReadonlyArray<T>, inserted: (newItem: T) => void, deleted: (oldItem: T) => void, compare?: Comparer<T>) {
compare = compare || compareValues;
let newIndex = 0;
let oldIndex = 0;
const newLen = newItems.length;
const oldLen = oldItems.length;
while (newIndex < newLen && oldIndex < oldLen) {
const newItem = newItems[newIndex];
const oldItem = oldItems[oldIndex];
const compareResult = compare(newItem, oldItem);
if (compareResult === Comparison.LessThan) {
inserted(newItem);
newIndex++;
}
else if (compareResult === Comparison.GreaterThan) {
deleted(oldItem);
oldIndex++;
}
else {
newIndex++;
oldIndex++;
}
}
while (newIndex < newLen) {
inserted(newItems[newIndex++]);
}
while (oldIndex < oldLen) {
deleted(oldItems[oldIndex++]);
}
}
}

Просмотреть файл

@ -260,11 +260,11 @@ namespace ts {
templateStack.pop();
}
else {
Debug.assert(token === SyntaxKind.TemplateMiddle, "Should have been a template middle. Was " + token);
Debug.assertEqual(token, SyntaxKind.TemplateMiddle, "Should have been a template middle.");
}
}
else {
Debug.assert(lastTemplateStackToken === SyntaxKind.OpenBraceToken, "Should have been an open brace. Was: " + token);
Debug.assertEqual(lastTemplateStackToken, SyntaxKind.OpenBraceToken, "Should have been an open brace");
templateStack.pop();
}
}

Просмотреть файл

@ -0,0 +1,27 @@
/* @internal */
namespace ts.codefix {
registerCodeFix({
errorCodes: [Diagnostics.Cannot_access_0_1_because_0_is_a_type_but_not_a_namespace_Did_you_mean_to_retrieve_the_type_of_the_property_1_in_0_with_0_1.code],
getCodeActions: (context: CodeFixContext) => {
const sourceFile = context.sourceFile;
const token = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false);
const qualifiedName = getAncestor(token, SyntaxKind.QualifiedName) as QualifiedName;
Debug.assert(!!qualifiedName, "Expected position to be owned by a qualified name.");
if (!isIdentifier(qualifiedName.left)) {
return undefined;
}
const leftText = qualifiedName.left.getText(sourceFile);
const rightText = qualifiedName.right.getText(sourceFile);
const replacement = createIndexedAccessTypeNode(
createTypeReferenceNode(qualifiedName.left, /*typeArguments*/ undefined),
createLiteralTypeNode(createLiteral(rightText)));
const changeTracker = textChanges.ChangeTracker.fromCodeFixContext(context);
changeTracker.replaceNode(sourceFile, qualifiedName, replacement);
return [{
description: formatStringFromArgs(getLocaleSpecificMessage(Diagnostics.Rewrite_as_the_indexed_access_type_0), [`${leftText}["${rightText}"]`]),
changes: changeTracker.getChanges()
}];
}
});
}

Просмотреть файл

@ -15,7 +15,8 @@ namespace ts.codefix {
const node = getTokenAtPosition(sourceFile, context.span.start, /*includeJsDocComment*/ false); // TODO: GH#15852
const checker = context.program.getTypeChecker();
let suggestion: string;
if (node.kind === SyntaxKind.Identifier && isPropertyAccessExpression(node.parent)) {
if (isPropertyAccessExpression(node.parent) && node.parent.name === node) {
Debug.assert(node.kind === SyntaxKind.Identifier);
const containingType = checker.getTypeAtLocation(node.parent.expression);
suggestion = checker.getSuggestionForNonexistentProperty(node as Identifier, containingType);
}

Просмотреть файл

@ -1,3 +1,4 @@
/// <reference path="correctQualifiedNameToIndexedAccessType.ts" />
/// <reference path="fixClassIncorrectlyImplementsInterface.ts" />
/// <reference path="fixAddMissingMember.ts" />
/// <reference path="fixSpelling.ts" />

Просмотреть файл

@ -147,7 +147,7 @@ namespace ts.codefix {
}
else if (isJsxOpeningLikeElement(token.parent) && token.parent.tagName === token) {
// The error wasn't for the symbolAtLocation, it was for the JSX tag itself, which needs access to e.g. `React`.
symbol = checker.getAliasedSymbol(checker.resolveNameAtLocation(token, checker.getJsxNamespace(), SymbolFlags.Value));
symbol = checker.getAliasedSymbol(checker.resolveName(checker.getJsxNamespace(), token.parent.tagName, SymbolFlags.Value));
symbolName = symbol.name;
}
else {
@ -174,7 +174,7 @@ namespace ts.codefix {
if (localSymbol && localSymbol.escapedName === name && checkSymbolHasMeaning(localSymbol, currentTokenMeaning)) {
// check if this symbol is already used
const symbolId = getUniqueSymbolId(localSymbol);
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, name, /*isDefault*/ true));
symbolIdActionMap.addActions(symbolId, getCodeActionForImport(moduleSymbol, name, /*isNamespaceImport*/ true));
}
}
@ -394,9 +394,11 @@ namespace ts.codefix {
: isNamespaceImport
? createImportClause(/*name*/ undefined, createNamespaceImport(createIdentifier(symbolName)))
: createImportClause(/*name*/ undefined, createNamedImports([createImportSpecifier(/*propertyName*/ undefined, createIdentifier(symbolName))]));
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, createLiteral(moduleSpecifierWithoutQuotes));
const moduleSpecifierLiteral = createLiteral(moduleSpecifierWithoutQuotes);
moduleSpecifierLiteral.singleQuote = getSingleQuoteStyleFromExistingImports();
const importDecl = createImportDeclaration(/*decorators*/ undefined, /*modifiers*/ undefined, importClause, moduleSpecifierLiteral);
if (!lastImportDeclaration) {
changeTracker.insertNodeAt(sourceFile, sourceFile.getStart(), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
changeTracker.insertNodeAt(sourceFile, getSourceFileImportLocation(sourceFile), importDecl, { suffix: `${context.newLineCharacter}${context.newLineCharacter}` });
}
else {
changeTracker.insertNodeAfter(sourceFile, lastImportDeclaration, importDecl, { suffix: context.newLineCharacter });
@ -413,6 +415,46 @@ namespace ts.codefix {
moduleSpecifierWithoutQuotes
);
function getSourceFileImportLocation(node: SourceFile) {
// For a source file, it is possible there are detached comments we should not skip
const text = node.text;
let ranges = getLeadingCommentRanges(text, 0);
if (!ranges) return 0;
let position = 0;
// However we should still skip a pinned comment at the top
if (ranges.length && ranges[0].kind === SyntaxKind.MultiLineCommentTrivia && isPinnedComment(text, ranges[0])) {
position = ranges[0].end + 1;
ranges = ranges.slice(1);
}
// As well as any triple slash references
for (const range of ranges) {
if (range.kind === SyntaxKind.SingleLineCommentTrivia && isRecognizedTripleSlashComment(node.text, range.pos, range.end)) {
position = range.end + 1;
continue;
}
break;
}
return position;
}
function getSingleQuoteStyleFromExistingImports() {
const firstModuleSpecifier = forEach(sourceFile.statements, node => {
if (isImportDeclaration(node) || isExportDeclaration(node)) {
if (node.moduleSpecifier && isStringLiteral(node.moduleSpecifier)) {
return node.moduleSpecifier;
}
}
else if (isImportEqualsDeclaration(node)) {
if (isExternalModuleReference(node.moduleReference) && isStringLiteral(node.moduleReference.expression)) {
return node.moduleReference.expression;
}
}
});
if (firstModuleSpecifier) {
return sourceFile.text.charCodeAt(firstModuleSpecifier.getStart()) === CharacterCodes.singleQuote;
}
}
function getModuleSpecifierForNewImport() {
const fileName = sourceFile.fileName;
const moduleFileName = moduleSymbol.valueDeclaration.getSourceFile().fileName;
@ -562,8 +604,8 @@ namespace ts.codefix {
function getNodeModulePathParts(fullPath: string) {
// If fullPath can't be valid module file within node_modules, returns undefined.
// Example of expected pattern: /base/path/node_modules/[otherpackage/node_modules/]package/[subdirectory/]file.js
// Returns indices: ^ ^ ^ ^
// Example of expected pattern: /base/path/node_modules/[@scope/otherpackage/@otherscope/node_modules/]package/[subdirectory/]file.js
// Returns indices: ^ ^ ^ ^
let topLevelNodeModulesIndex = 0;
let topLevelPackageNameIndex = 0;
@ -573,6 +615,7 @@ namespace ts.codefix {
const enum States {
BeforeNodeModules,
NodeModules,
Scope,
PackageContent
}
@ -592,8 +635,14 @@ namespace ts.codefix {
}
break;
case States.NodeModules:
packageRootIndex = partEnd;
state = States.PackageContent;
case States.Scope:
if (state === States.NodeModules && fullPath.charAt(partStart + 1) === "@") {
state = States.Scope;
}
else {
packageRootIndex = partEnd;
state = States.PackageContent;
}
break;
case States.PackageContent:
if (fullPath.indexOf("/node_modules/", partStart) === partStart) {

Просмотреть файл

@ -1000,7 +1000,7 @@ namespace ts.Completions {
const typeForObject = typeChecker.getTypeAtLocation(objectLikeContainer);
if (!typeForObject) return false;
// In a binding pattern, get only known properties. Everywhere else we will get all possible properties.
typeMembers = typeChecker.getPropertiesOfType(typeForObject);
typeMembers = typeChecker.getPropertiesOfType(typeForObject).filter((symbol) => !(getDeclarationModifierFlagsFromSymbol(symbol) & ModifierFlags.NonPublicAccessibilityModifier));
existingMembers = (<ObjectBindingPattern>objectLikeContainer).elements;
}
}

Просмотреть файл

@ -604,11 +604,16 @@ namespace ts.FindAllReferences.Core {
function getPropertySymbolOfObjectBindingPatternWithoutPropertyName(symbol: Symbol, checker: TypeChecker): Symbol | undefined {
const bindingElement = getObjectBindingElementWithoutPropertyName(symbol);
if (bindingElement) {
const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
return typeOfPattern && checker.getPropertyOfType(typeOfPattern, (<Identifier>bindingElement.name).text);
if (!bindingElement) return undefined;
const typeOfPattern = checker.getTypeAtLocation(bindingElement.parent);
const propSymbol = typeOfPattern && checker.getPropertyOfType(typeOfPattern, (<Identifier>bindingElement.name).text);
if (propSymbol && propSymbol.flags & SymbolFlags.Accessor) {
// See GH#16922
Debug.assert(!!(propSymbol.flags & SymbolFlags.Transient));
return (propSymbol as TransientSymbol).target;
}
return undefined;
return propSymbol;
}
/**
@ -647,10 +652,15 @@ namespace ts.FindAllReferences.Core {
return undefined;
}
// If the symbol has a parent, it's globally visible.
// Unless that parent is an external module, then we should only search in the module (and recurse on the export later).
// But if the parent is a module that has `export as namespace`, then the symbol *is* globally visible.
if (parent && !((parent.flags & SymbolFlags.Module) && isExternalModuleSymbol(parent) && !parent.globalExports)) {
/*
If the symbol has a parent, it's globally visible unless:
- It's a private property (handled above).
- It's a type parameter.
- The parent is an external module: then we should only search in the module (and recurse on the export later).
- But if the parent has `export as namespace`, the symbol is globally visible through that namespace.
*/
const exposedByParent = parent && !(symbol.flags & SymbolFlags.TypeParameter);
if (exposedByParent && !((parent.flags & SymbolFlags.Module) && isExternalModuleSymbol(parent) && !parent.globalExports)) {
return undefined;
}
@ -677,7 +687,7 @@ namespace ts.FindAllReferences.Core {
// declare module "a" { export type T = number; }
// declare module "b" { import { T } from "a"; export const x: T; }
// So we must search the whole source file. (Because we will mark the source file as seen, we we won't return to it when searching for imports.)
return parent ? scope.getSourceFile() : scope;
return exposedByParent ? scope.getSourceFile() : scope;
}
function getPossibleSymbolReferencePositions(sourceFile: SourceFile, symbolName: string, container: Node = sourceFile): number[] {

Просмотреть файл

@ -398,7 +398,6 @@ namespace ts.formatting {
// formatting context is used by rules provider
const formattingContext = new FormattingContext(sourceFile, requestKind, options);
let previousRangeHasError: boolean;
let previousRange: TextRangeWithKind;
let previousParent: Node;
let previousRangeStartLine: number;
@ -883,7 +882,7 @@ namespace ts.formatting {
const rangeHasError = rangeContainsError(range);
let lineAdded: boolean;
if (!rangeHasError && !previousRangeHasError) {
if (!rangeHasError) {
if (!previousRange) {
// trim whitespaces starting from the beginning of the span up to the current line
const originalStart = sourceFile.getLineAndCharacterOfPosition(originalRange.pos);
@ -898,7 +897,6 @@ namespace ts.formatting {
previousRange = range;
previousParent = parent;
previousRangeStartLine = rangeStart.line;
previousRangeHasError = rangeHasError;
return lineAdded;
}
@ -1152,6 +1150,56 @@ namespace ts.formatting {
}
}
/**
* @param precedingToken pass `null` if preceding token was already computed and result was `undefined`.
*/
export function getRangeOfEnclosingComment(
sourceFile: SourceFile,
position: number,
onlyMultiLine: boolean,
precedingToken?: Node | null, // tslint:disable-line:no-null-keyword
tokenAtPosition = getTokenAtPosition(sourceFile, position, /*includeJsDocComment*/ false),
predicate?: (c: CommentRange) => boolean): CommentRange | undefined {
const tokenStart = tokenAtPosition.getStart(sourceFile);
if (tokenStart <= position && position < tokenAtPosition.getEnd()) {
return undefined;
}
if (precedingToken === undefined) {
precedingToken = findPrecedingToken(position, sourceFile);
}
// Between two consecutive tokens, all comments are either trailing on the former
// or leading on the latter (and none are in both lists).
const trailingRangesOfPreviousToken = precedingToken && getTrailingCommentRanges(sourceFile.text, precedingToken.end);
const leadingCommentRangesOfNextToken = getLeadingCommentRangesOfNode(tokenAtPosition, sourceFile);
const commentRanges = trailingRangesOfPreviousToken && leadingCommentRangesOfNextToken ?
trailingRangesOfPreviousToken.concat(leadingCommentRangesOfNextToken) :
trailingRangesOfPreviousToken || leadingCommentRangesOfNextToken;
if (commentRanges) {
for (const range of commentRanges) {
// The end marker of a single-line comment does not include the newline character.
// With caret at `^`, in the following case, we are inside a comment (^ denotes the cursor position):
//
// // asdf ^\n
//
// But for closed multi-line comments, we don't want to be inside the comment in the following case:
//
// /* asdf */^
//
// However, unterminated multi-line comments *do* contain their end.
//
// Internally, we represent the end of the comment at the newline and closing '/', respectively.
//
if ((range.pos < position && position < range.end ||
position === range.end && (range.kind === SyntaxKind.SingleLineCommentTrivia || position === sourceFile.getFullWidth()))) {
return (range.kind === SyntaxKind.MultiLineCommentTrivia || !onlyMultiLine) && (!predicate || predicate(range)) ? range : undefined;
}
}
}
return undefined;
}
function getOpenTokenForList(node: Node, list: ReadonlyArray<Node>) {
switch (node.kind) {
case SyntaxKind.Constructor:

Просмотреть файл

@ -195,6 +195,7 @@ namespace ts.formatting {
// Insert space after opening and before closing nonempty parenthesis
public SpaceAfterOpenParen: Rule;
public SpaceBeforeCloseParen: Rule;
public SpaceBetweenOpenParens: Rule;
public NoSpaceBetweenParens: Rule;
public NoSpaceAfterOpenParen: Rule;
public NoSpaceBeforeCloseParen: Rule;
@ -457,6 +458,7 @@ namespace ts.formatting {
// Insert space after opening and before closing nonempty parenthesis
this.SpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.SpaceBetweenOpenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.OpenParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionEnabled("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Space));
this.NoSpaceBetweenParens = new Rule(RuleDescriptor.create1(SyntaxKind.OpenParenToken, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceAfterOpenParen = new Rule(RuleDescriptor.create3(SyntaxKind.OpenParenToken, Shared.TokenRange.Any), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
this.NoSpaceBeforeCloseParen = new Rule(RuleDescriptor.create2(Shared.TokenRange.Any, SyntaxKind.CloseParenToken), RuleOperation.create2(new RuleOperationContext(Rules.IsOptionDisabledOrUndefined("insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis"), Rules.IsNonJsxSameLineTokenContext), RuleAction.Delete));
@ -544,7 +546,7 @@ namespace ts.formatting {
this.SpaceAfterComma, this.NoSpaceAfterComma,
this.SpaceAfterAnonymousFunctionKeyword, this.NoSpaceAfterAnonymousFunctionKeyword,
this.SpaceAfterKeywordInControl, this.NoSpaceAfterKeywordInControl,
this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen,
this.SpaceAfterOpenParen, this.SpaceBeforeCloseParen, this.SpaceBetweenOpenParens, this.NoSpaceBetweenParens, this.NoSpaceAfterOpenParen, this.NoSpaceBeforeCloseParen,
this.SpaceAfterOpenBracket, this.SpaceBeforeCloseBracket, this.NoSpaceBetweenBrackets, this.NoSpaceAfterOpenBracket, this.NoSpaceBeforeCloseBracket,
this.SpaceAfterOpenBrace, this.SpaceBeforeCloseBrace, this.NoSpaceBetweenEmptyBraceBrackets, this.NoSpaceAfterOpenBrace, this.NoSpaceBeforeCloseBrace,
this.SpaceAfterTemplateHeadAndMiddle, this.SpaceBeforeTemplateMiddleAndTail, this.NoSpaceAfterTemplateHeadAndMiddle, this.NoSpaceBeforeTemplateMiddleAndTail,

Просмотреть файл

@ -32,13 +32,36 @@ namespace ts.formatting {
}
const precedingToken = findPrecedingToken(position, sourceFile);
const enclosingCommentRange = getRangeOfEnclosingComment(sourceFile, position, /*onlyMultiLine*/ true, precedingToken || null); // tslint:disable-line:no-null-keyword
if (enclosingCommentRange) {
const previousLine = getLineAndCharacterOfPosition(sourceFile, position).line - 1;
const commentStartLine = getLineAndCharacterOfPosition(sourceFile, enclosingCommentRange.pos).line;
Debug.assert(commentStartLine >= 0);
if (previousLine <= commentStartLine) {
return findFirstNonWhitespaceColumn(getStartPositionOfLine(commentStartLine, sourceFile), position, sourceFile, options);
}
const startPostionOfLine = getStartPositionOfLine(previousLine, sourceFile);
const { column, character } = findFirstNonWhitespaceCharacterAndColumn(startPostionOfLine, position, sourceFile, options);
if (column === 0) {
return column;
}
const firstNonWhitespaceCharacterCode = sourceFile.text.charCodeAt(startPostionOfLine + character);
return firstNonWhitespaceCharacterCode === CharacterCodes.asterisk ? column - 1 : column;
}
if (!precedingToken) {
return getBaseIndentation(options);
}
// no indentation in string \regex\template literals
const precedingTokenIsLiteral = isStringOrRegularExpressionOrTemplateLiteral(precedingToken.kind);
if (precedingTokenIsLiteral && precedingToken.getStart(sourceFile) <= position && precedingToken.end > position) {
if (precedingTokenIsLiteral && precedingToken.getStart(sourceFile) <= position && position < precedingToken.end) {
return 0;
}
@ -405,13 +428,13 @@ namespace ts.formatting {
return findFirstNonWhitespaceColumn(lineStart, lineStart + lineAndCharacter.character, sourceFile, options);
}
/*
Character is the actual index of the character since the beginning of the line.
Column - position of the character after expanding tabs to spaces
"0\t2$"
value of 'character' for '$' is 3
value of 'column' for '$' is 6 (assuming that tab size is 4)
*/
/**
* Character is the actual index of the character since the beginning of the line.
* Column - position of the character after expanding tabs to spaces.
* "0\t2$"
* value of 'character' for '$' is 3
* value of 'column' for '$' is 6 (assuming that tab size is 4)
*/
export function findFirstNonWhitespaceCharacterAndColumn(startPos: number, endPos: number, sourceFile: SourceFileLike, options: EditorSettings) {
let character = 0;
let column = 0;

Некоторые файлы не были показаны из-за слишком большого количества измененных файлов Показать больше