Merge branch 'master' into incrementalBuildInfo

This commit is contained in:
Sheetal Nandi
2019-03-08 11:42:19 -08:00
117 changed files with 4728 additions and 5649 deletions

View File

@@ -47,23 +47,6 @@ interface XMLHttpRequest {
/* tslint:enable:no-var-keyword prefer-const */
namespace Utils {
// Setup some globals based on the current environment
export const enum ExecutionEnvironment {
Node,
Browser,
}
export function getExecutionEnvironment() {
if (typeof window !== "undefined") {
return ExecutionEnvironment.Browser;
}
else {
return ExecutionEnvironment.Node;
}
}
export let currentExecutionEnvironment = getExecutionEnvironment();
export function encodeString(s: string): string {
return ts.sys.bufferFrom!(s).toString("utf8");
}
@@ -74,23 +57,12 @@ namespace Utils {
}
export function evalFile(fileContents: string, fileName: string, nodeContext?: any) {
const environment = getExecutionEnvironment();
switch (environment) {
case ExecutionEnvironment.Browser:
// tslint:disable-next-line:no-eval
eval(fileContents);
break;
case ExecutionEnvironment.Node:
const vm = require("vm");
if (nodeContext) {
vm.runInNewContext(fileContents, nodeContext, fileName);
}
else {
vm.runInThisContext(fileContents, fileName);
}
break;
default:
throw new Error("Unknown context");
const vm = require("vm");
if (nodeContext) {
vm.runInNewContext(fileContents, nodeContext, fileName);
}
else {
vm.runInThisContext(fileContents, fileName);
}
}
@@ -624,344 +596,11 @@ namespace Harness {
};
}
interface URL {
hash: string;
host: string;
hostname: string;
href: string;
password: string;
pathname: string;
port: string;
protocol: string;
search: string;
username: string;
toString(): string;
}
declare var URL: {
prototype: URL;
new(url: string, base?: string | URL): URL;
};
function createBrowserIO(): IO {
const serverRoot = new URL("http://localhost:8888/");
class HttpHeaders extends collections.SortedMap<string, string | string[]> {
constructor(template?: Record<string, string | string[]>) {
super(ts.compareStringsCaseInsensitive);
if (template) {
for (const key in template) {
if (ts.hasProperty(template, key)) {
this.set(key, template[key]);
}
}
}
}
public static combine(left: HttpHeaders | undefined, right: HttpHeaders | undefined): HttpHeaders | undefined {
if (!left && !right) return undefined;
const headers = new HttpHeaders();
if (left) left.forEach((value, key) => { headers.set(key, value); });
if (right) right.forEach((value, key) => { headers.set(key, value); });
return headers;
}
public has(key: string) {
return super.has(key.toLowerCase());
}
public get(key: string) {
return super.get(key.toLowerCase());
}
public set(key: string, value: string | string[]) {
return super.set(key.toLowerCase(), value);
}
public delete(key: string) {
return super.delete(key.toLowerCase());
}
public writeRequestHeaders(xhr: XMLHttpRequest) {
this.forEach((values, key) => {
if (key === "access-control-allow-origin" || key === "content-length") return;
const value = Array.isArray(values) ? values.join(",") : values;
if (key === "content-type") {
xhr.overrideMimeType(value);
return;
}
xhr.setRequestHeader(key, value);
});
}
public static readResponseHeaders(xhr: XMLHttpRequest): HttpHeaders {
const allHeaders = xhr.getAllResponseHeaders();
const headers = new HttpHeaders();
for (const header of allHeaders.split(/\r\n/g)) {
const colonIndex = header.indexOf(":");
if (colonIndex >= 0) {
const key = header.slice(0, colonIndex).trim();
const value = header.slice(colonIndex + 1).trim();
const values = value.split(",");
headers.set(key, values.length > 1 ? values : value);
}
}
return headers;
}
}
class HttpContent {
public headers: HttpHeaders;
public content: string;
constructor(headers: HttpHeaders | Record<string, string | string[]>, content: string) {
this.headers = headers instanceof HttpHeaders ? headers : new HttpHeaders(headers);
this.content = content;
}
public static fromMediaType(mediaType: string, content: string) {
return new HttpContent({ "Content-Type": mediaType }, content);
}
public static text(content: string) {
return HttpContent.fromMediaType("text/plain", content);
}
public static json(content: object) {
return HttpContent.fromMediaType("application/json", JSON.stringify(content));
}
public static readResponseContent(xhr: XMLHttpRequest) {
if (typeof xhr.responseText === "string") {
return new HttpContent({
"Content-Type": xhr.getResponseHeader("Content-Type") || undefined!, // TODO: GH#18217
"Content-Length": xhr.getResponseHeader("Content-Length") || undefined!, // TODO: GH#18217
}, xhr.responseText);
}
return undefined;
}
public writeRequestHeaders(xhr: XMLHttpRequest) {
this.headers.writeRequestHeaders(xhr);
}
}
class HttpRequestMessage {
public method: string;
public url: URL;
public headers: HttpHeaders;
public content?: HttpContent;
constructor(method: string, url: string | URL, headers?: HttpHeaders | Record<string, string | string[]>, content?: HttpContent) {
this.method = method;
this.url = typeof url === "string" ? new URL(url) : url;
this.headers = headers instanceof HttpHeaders ? headers : new HttpHeaders(headers);
this.content = content;
}
public static options(url: string | URL) {
return new HttpRequestMessage("OPTIONS", url);
}
public static head(url: string | URL) {
return new HttpRequestMessage("HEAD", url);
}
public static get(url: string | URL) {
return new HttpRequestMessage("GET", url);
}
public static delete(url: string | URL) {
return new HttpRequestMessage("DELETE", url);
}
public static put(url: string | URL, content: HttpContent) {
return new HttpRequestMessage("PUT", url, /*headers*/ undefined, content);
}
public static post(url: string | URL, content: HttpContent) {
return new HttpRequestMessage("POST", url, /*headers*/ undefined, content);
}
public writeRequestHeaders(xhr: XMLHttpRequest) {
this.headers.writeRequestHeaders(xhr);
if (this.content) {
this.content.writeRequestHeaders(xhr);
}
}
}
class HttpResponseMessage {
public statusCode: number;
public statusMessage: string;
public headers: HttpHeaders;
public content?: HttpContent;
constructor(statusCode: number, statusMessage: string, headers?: HttpHeaders | Record<string, string | string[]>, content?: HttpContent) {
this.statusCode = statusCode;
this.statusMessage = statusMessage;
this.headers = headers instanceof HttpHeaders ? headers : new HttpHeaders(headers);
this.content = content;
}
public static notFound(): HttpResponseMessage {
return new HttpResponseMessage(404, "Not Found");
}
public static hasSuccessStatusCode(response: HttpResponseMessage) {
return response.statusCode === 304 || (response.statusCode >= 200 && response.statusCode < 300);
}
public static readResponseMessage(xhr: XMLHttpRequest) {
return new HttpResponseMessage(
xhr.status,
xhr.statusText,
HttpHeaders.readResponseHeaders(xhr),
HttpContent.readResponseContent(xhr));
}
}
function send(request: HttpRequestMessage): HttpResponseMessage {
const xhr = new XMLHttpRequest();
try {
xhr.open(request.method, request.url.toString(), /*async*/ false);
request.writeRequestHeaders(xhr);
xhr.setRequestHeader("Access-Control-Allow-Origin", "*");
xhr.send(request.content && request.content.content);
while (xhr.readyState !== 4); // block until ready
return HttpResponseMessage.readResponseMessage(xhr);
}
catch (e) {
return HttpResponseMessage.notFound();
}
}
let caseSensitivity: "CI" | "CS" | undefined;
function useCaseSensitiveFileNames() {
if (!caseSensitivity) {
const response = send(HttpRequestMessage.options(new URL("*", serverRoot)));
const xCaseSensitivity = response.headers.get("X-Case-Sensitivity");
caseSensitivity = xCaseSensitivity === "CS" ? "CS" : "CI";
}
return caseSensitivity === "CS";
}
function resolvePath(path: string) {
const response = send(HttpRequestMessage.post(new URL("/api/resolve", serverRoot), HttpContent.text(path)));
return HttpResponseMessage.hasSuccessStatusCode(response) && response.content ? response.content.content : undefined;
}
function getFileSize(path: string): number {
const response = send(HttpRequestMessage.head(new URL(path, serverRoot)));
return HttpResponseMessage.hasSuccessStatusCode(response) ? +response.headers.get("Content-Length")!.toString() : 0;
}
function readFile(path: string): string | undefined {
const response = send(HttpRequestMessage.get(new URL(path, serverRoot)));
return HttpResponseMessage.hasSuccessStatusCode(response) && response.content ? response.content.content : undefined;
}
function writeFile(path: string, contents: string) {
send(HttpRequestMessage.put(new URL(path, serverRoot), HttpContent.text(contents)));
}
function fileExists(path: string): boolean {
const response = send(HttpRequestMessage.head(new URL(path, serverRoot)));
return HttpResponseMessage.hasSuccessStatusCode(response);
}
function directoryExists(path: string): boolean {
const response = send(HttpRequestMessage.post(new URL("/api/directoryExists", serverRoot), HttpContent.text(path)));
return hasJsonContent(response) && JSON.parse(response.content.content) as boolean;
}
function deleteFile(path: string) {
send(HttpRequestMessage.delete(new URL(path, serverRoot)));
}
function directoryName(path: string) {
const url = new URL(path, serverRoot);
return ts.getDirectoryPath(ts.normalizeSlashes(url.pathname || "/"));
}
function enumerateTestFiles(runner: RunnerBase): (string | FileBasedTest)[] {
const response = send(HttpRequestMessage.post(new URL("/api/enumerateTestFiles", serverRoot), HttpContent.text(runner.kind())));
return hasJsonContent(response) ? JSON.parse(response.content.content) : [];
}
function listFiles(dirname: string, spec?: RegExp, options?: { recursive?: boolean }): string[] {
if (spec || (options && !options.recursive)) {
let results = IO.listFiles(dirname);
if (spec) {
results = results.filter(file => spec.test(file));
}
if (options && !options.recursive) {
results = results.filter(file => ts.getDirectoryPath(ts.normalizeSlashes(file)) === dirname);
}
return results;
}
const response = send(HttpRequestMessage.post(new URL("/api/listFiles", serverRoot), HttpContent.text(dirname)));
return hasJsonContent(response) ? JSON.parse(response.content.content) : [];
}
function readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[], depth?: number) {
return ts.matchFiles(path, extension, exclude, include, useCaseSensitiveFileNames(), "", depth, getAccessibleFileSystemEntries);
}
function getAccessibleFileSystemEntries(dirname: string): ts.FileSystemEntries {
const response = send(HttpRequestMessage.post(new URL("/api/getAccessibleFileSystemEntries", serverRoot), HttpContent.text(dirname)));
return hasJsonContent(response) ? JSON.parse(response.content.content) : { files: [], directories: [] };
}
function hasJsonContent(response: HttpResponseMessage): response is HttpResponseMessage & { content: HttpContent } {
return HttpResponseMessage.hasSuccessStatusCode(response)
&& !!response.content
&& /^application\/json(;.*)$/.test("" + response.content.headers.get("Content-Type"));
}
return {
newLine: () => harnessNewLine,
getCurrentDirectory: () => "",
useCaseSensitiveFileNames,
resolvePath,
getFileSize,
readFile,
writeFile,
directoryName: Utils.memoize(directoryName, path => path),
getDirectories: () => [],
createDirectory: () => {}, // tslint:disable-line no-empty
fileExists,
directoryExists,
deleteFile,
listFiles: Utils.memoize(listFiles, (path, spec, options) => `${path}|${spec}|${options ? options.recursive === true : true}`),
enumerateTestFiles: Utils.memoize(enumerateTestFiles, runner => runner.kind()),
log: s => console.log(s),
args: () => [],
getExecutingFilePath: () => "",
exit: () => {}, // tslint:disable-line no-empty
readDirectory,
getAccessibleFileSystemEntries,
getWorkspaceRoot: () => "/"
};
}
export function mockHash(s: string): string {
return `hash-${s}`;
}
const environment = Utils.getExecutionEnvironment();
switch (environment) {
case Utils.ExecutionEnvironment.Node:
IO = createNodeIO();
break;
case Utils.ExecutionEnvironment.Browser:
IO = createBrowserIO();
break;
default:
throw new Error(`Unknown value '${environment}' for ExecutionEnvironment.`);
}
IO = createNodeIO();
}
if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.getEnvironmentVariable!("NODE_ENV"))) {
@@ -970,10 +609,8 @@ if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.ge
namespace Harness {
export const libFolder = "built/local/";
const tcServicesFileName = ts.combinePaths(libFolder, Utils.getExecutionEnvironment() === Utils.ExecutionEnvironment.Browser ? "typescriptServicesInBrowserTest.js" : "typescriptServices.js");
export const tcServicesFile = IO.readFile(tcServicesFileName) + (Utils.getExecutionEnvironment() !== Utils.ExecutionEnvironment.Browser
? IO.newLine() + `//# sourceURL=${IO.resolvePath(tcServicesFileName)}`
: "");
const tcServicesFileName = ts.combinePaths(libFolder, "typescriptServices.js");
export const tcServicesFile = IO.readFile(tcServicesFileName) + IO.newLine() + `//# sourceURL=${IO.resolvePath(tcServicesFileName)}`;
export type SourceMapEmitterCallback = (
emittedFile: string,