mirror of
https://github.com/microsoft/TypeScript.git
synced 2026-05-20 22:51:17 -05:00
Merge branch 'master' into incrementalBuildInfo
This commit is contained in:
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user