Merge branch 'master' into no_dom

This commit is contained in:
Andy Hanson
2017-01-17 14:35:33 -08:00
211 changed files with 7820 additions and 4046 deletions
+57 -37
View File
@@ -40,7 +40,7 @@ namespace FourSlash {
files: FourSlashFile[];
// A mapping from marker names to name/position pairs
markerPositions: { [index: string]: Marker; };
markerPositions: ts.Map<Marker>;
markers: Marker[];
@@ -61,10 +61,6 @@ namespace FourSlash {
data?: any;
}
interface MarkerMap {
[index: string]: Marker;
}
export interface Range {
fileName: string;
start: number;
@@ -94,7 +90,7 @@ namespace FourSlash {
export import IndentStyle = ts.IndentStyle;
const entityMap = ts.createMap({
const entityMap = ts.createMapFromTemplate({
"&": "&amp;",
"\"": "&quot;",
"'": "&#39;",
@@ -104,7 +100,7 @@ namespace FourSlash {
});
export function escapeXmlAttributeValue(s: string) {
return s.replace(/[&<>"'\/]/g, ch => entityMap[ch]);
return s.replace(/[&<>"'\/]/g, ch => entityMap.get(ch));
}
// Name of testcase metadata including ts.CompilerOptions properties that will be used by globalOptions
@@ -230,7 +226,7 @@ namespace FourSlash {
}
function tryAdd(path: string) {
const inputFile = inputFiles[path];
const inputFile = inputFiles.get(path);
if (inputFile && !Harness.isDefaultLibraryFile(path)) {
languageServiceAdapterHost.addScript(path, inputFile, /*isRootFile*/ true);
return true;
@@ -265,7 +261,7 @@ namespace FourSlash {
let configFileName: string;
ts.forEach(testData.files, file => {
// Create map between fileName and its content for easily looking up when resolveReference flag is specified
this.inputFiles[file.fileName] = file.content;
this.inputFiles.set(file.fileName, file.content);
if (ts.getBaseFileName(file.fileName).toLowerCase() === "tsconfig.json") {
configFileName = file.fileName;
}
@@ -283,7 +279,7 @@ namespace FourSlash {
const baseDir = ts.normalizePath(ts.getDirectoryPath(configFileName));
const host = new Utils.MockParseConfigHost(baseDir, /*ignoreCase*/ false, this.inputFiles);
const configJsonObj = ts.parseConfigFileTextToJson(configFileName, this.inputFiles[configFileName]);
const configJsonObj = ts.parseConfigFileTextToJson(configFileName, this.inputFiles.get(configFileName));
assert.isTrue(configJsonObj.config !== undefined);
const { options, errors } = ts.parseJsonConfigFileContent(configJsonObj.config, host, baseDir);
@@ -334,11 +330,11 @@ namespace FourSlash {
}
else {
// resolveReference file-option is not specified then do not resolve any files and include all inputFiles
for (const fileName in this.inputFiles) {
this.inputFiles.forEach((file, fileName) => {
if (!Harness.isDefaultLibraryFile(fileName)) {
this.languageServiceAdapterHost.addScript(fileName, this.inputFiles[fileName], /*isRootFile*/ true);
this.languageServiceAdapterHost.addScript(fileName, file, /*isRootFile*/ true);
}
}
});
this.languageServiceAdapterHost.addScript(Harness.Compiler.defaultLibFileName,
Harness.Compiler.getDefaultLibrarySourceFile().text, /*isRootFile*/ false);
}
@@ -425,8 +421,7 @@ namespace FourSlash {
}
private raiseError(message: string) {
message = this.messageAtLastKnownMarker(message);
throw new Error(message);
throw new Error(this.messageAtLastKnownMarker(message));
}
private messageAtLastKnownMarker(message: string) {
@@ -685,11 +680,12 @@ namespace FourSlash {
const completions = this.getCompletionListAtCaret();
const uniqueItems = ts.createMap<string>();
for (const item of completions.entries) {
if (!(item.name in uniqueItems)) {
uniqueItems[item.name] = item.kind;
const uniqueItem = uniqueItems.get(item.name);
if (!uniqueItem) {
uniqueItems.set(item.name, item.kind);
}
else {
assert.equal(item.kind, uniqueItems[item.name], `Items should have the same kind, got ${item.kind} and ${uniqueItems[item.name]}`);
assert.equal(item.kind, uniqueItem, `Items should have the same kind, got ${item.kind} and ${uniqueItem}`);
}
}
}
@@ -723,6 +719,27 @@ namespace FourSlash {
}
}
public verifyCompletionsAt(markerName: string, expected: string[]) {
this.goToMarker(markerName);
const actualCompletions = this.getCompletionListAtCaret();
if (!actualCompletions) {
this.raiseError(`No completions at position '${this.currentCaretPosition}'.`);
}
const actual = actualCompletions.entries;
if (actual.length !== expected.length) {
this.raiseError(`Expected ${expected.length} completions, got ${actual.map(a => a.name)}.`);
}
ts.zipWith(actual, expected, (completion, expectedCompletion, index) => {
if (completion.name !== expectedCompletion) {
this.raiseError(`Expected completion at index ${index} to be ${expectedCompletion}, got ${completion.name}`);
}
});
}
public verifyCompletionListContains(symbol: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
const completions = this.getCompletionListAtCaret();
if (completions) {
@@ -857,7 +874,7 @@ namespace FourSlash {
}
public verifyRangesWithSameTextReferenceEachOther() {
ts.forEachProperty(this.rangesByText(), ranges => this.verifyRangesReferenceEachOther(ranges));
this.rangesByText().forEach(ranges => this.verifyRangesReferenceEachOther(ranges));
}
public verifyDisplayPartsOfReferencedSymbol(expected: ts.SymbolDisplayPart[]) {
@@ -929,7 +946,8 @@ namespace FourSlash {
}
public verifyQuickInfos(namesAndTexts: { [name: string]: string | [string, string] }) {
ts.forEachProperty(ts.createMap(namesAndTexts), (text, name) => {
for (const name in namesAndTexts) if (ts.hasProperty(namesAndTexts, name)) {
const text = namesAndTexts[name];
if (text instanceof Array) {
assert(text.length === 2);
const [expectedText, expectedDocumentation] = text;
@@ -938,7 +956,7 @@ namespace FourSlash {
else {
this.verifyQuickInfoAt(name, text);
}
});
}
}
public verifyQuickInfoString(expectedText: string, expectedDocumentation?: string) {
@@ -1776,7 +1794,7 @@ namespace FourSlash {
}
public getMarkerNames(): string[] {
return Object.keys(this.testData.markerPositions);
return ts.arrayFrom(this.testData.markerPositions.keys());
}
public getRanges(): Range[] {
@@ -1784,10 +1802,10 @@ namespace FourSlash {
}
public rangesByText(): ts.Map<Range[]> {
const result = ts.createMap<Range[]>();
const result = ts.createMultiMap<Range>();
for (const range of this.getRanges()) {
const text = this.rangeText(range);
ts.multiMapAdd(result, text, range);
result.add(text, range);
}
return result;
}
@@ -2143,7 +2161,7 @@ namespace FourSlash {
public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) {
const openBraceMap = ts.createMap<ts.CharacterCodes>({
const openBraceMap = ts.createMapFromTemplate<ts.CharacterCodes>({
"(": ts.CharacterCodes.openParen,
"{": ts.CharacterCodes.openBrace,
"[": ts.CharacterCodes.openBracket,
@@ -2153,7 +2171,7 @@ namespace FourSlash {
"<": ts.CharacterCodes.lessThan
});
const charCode = openBraceMap[openingBrace];
const charCode = openBraceMap.get(openingBrace);
if (!charCode) {
this.raiseError(`Invalid openingBrace '${openingBrace}' specified.`);
@@ -2509,11 +2527,9 @@ namespace FourSlash {
}
public getMarkerByName(markerName: string) {
const markerPos = this.testData.markerPositions[markerName];
const markerPos = this.testData.markerPositions.get(markerName);
if (markerPos === undefined) {
const markerNames: string[] = [];
for (const m in this.testData.markerPositions) markerNames.push(m);
throw new Error(`Unknown marker "${markerName}" Available markers: ${markerNames.map(m => "\"" + m + "\"").join(", ")}`);
throw new Error(`Unknown marker "${markerName}" Available markers: ${this.getMarkerNames().map(m => "\"" + m + "\"").join(", ")}`);
}
else {
return markerPos;
@@ -2606,7 +2622,7 @@ ${code}
// we have to string-based splitting instead and try to figure out the delimiting chars
const lines = contents.split("\n");
const markerPositions: MarkerMap = {};
const markerPositions = ts.createMap<Marker>();
const markers: Marker[] = [];
const ranges: Range[] = [];
@@ -2745,7 +2761,7 @@ ${code}
throw new Error(errorMessage);
}
function recordObjectMarker(fileName: string, location: LocationInformation, text: string, markerMap: MarkerMap, markers: Marker[]): Marker {
function recordObjectMarker(fileName: string, location: LocationInformation, text: string, markerMap: ts.Map<Marker>, markers: Marker[]): Marker {
let markerValue: any = undefined;
try {
// Attempt to parse the marker value as JSON
@@ -2768,7 +2784,7 @@ ${code}
// Object markers can be anonymous
if (markerValue.name) {
markerMap[markerValue.name] = marker;
markerMap.set(markerValue.name, marker);
}
markers.push(marker);
@@ -2776,26 +2792,26 @@ ${code}
return marker;
}
function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: MarkerMap, markers: Marker[]): Marker {
function recordMarker(fileName: string, location: LocationInformation, name: string, markerMap: ts.Map<Marker>, markers: Marker[]): Marker {
const marker: Marker = {
fileName,
position: location.position
};
// Verify markers for uniqueness
if (markerMap[name] !== undefined) {
if (markerMap.has(name)) {
const message = "Marker '" + name + "' is duplicated in the source file contents.";
reportError(marker.fileName, location.sourceLine, location.sourceColumn, message);
return undefined;
}
else {
markerMap[name] = marker;
markerMap.set(name, marker);
markers.push(marker);
return marker;
}
}
function parseFileContent(content: string, fileName: string, markerMap: MarkerMap, markers: Marker[], ranges: Range[]): FourSlashFile {
function parseFileContent(content: string, fileName: string, markerMap: ts.Map<Marker>, markers: Marker[], ranges: Range[]): FourSlashFile {
content = chompLeadingSpace(content);
// Any slash-star comment with a character not in this string is not a marker.
@@ -3166,6 +3182,10 @@ namespace FourSlashInterface {
super(state);
}
public completionsAt(markerName: string, completions: string[]) {
this.state.verifyCompletionsAt(markerName, completions);
}
public quickInfoIs(expectedText: string, expectedDocumentation?: string) {
this.state.verifyQuickInfoString(expectedText, expectedDocumentation);
}
+9 -8
View File
@@ -923,7 +923,7 @@ namespace Harness {
export const defaultLibFileName = "lib.d.ts";
export const es2015DefaultLibFileName = "lib.es2015.d.ts";
const libFileNameSourceFileMap = ts.createMap<ts.SourceFile>({
const libFileNameSourceFileMap = ts.createMapFromTemplate<ts.SourceFile>({
[defaultLibFileName]: createSourceFileAndAssertInvariants(defaultLibFileName, IO.readFile(libFolder + "lib.es5.d.ts"), /*languageVersion*/ ts.ScriptTarget.Latest)
});
@@ -932,10 +932,11 @@ namespace Harness {
return undefined;
}
if (!libFileNameSourceFileMap[fileName]) {
libFileNameSourceFileMap[fileName] = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest);
let sourceFile = libFileNameSourceFileMap.get(fileName);
if (!sourceFile) {
libFileNameSourceFileMap.set(fileName, sourceFile = createSourceFileAndAssertInvariants(fileName, IO.readFile(libFolder + fileName), ts.ScriptTarget.Latest));
}
return libFileNameSourceFileMap[fileName];
return sourceFile;
}
export function getDefaultLibFileName(options: ts.CompilerOptions): string {
@@ -1117,10 +1118,10 @@ namespace Harness {
optionsIndex = ts.createMap<ts.CommandLineOption>();
const optionDeclarations = harnessOptionDeclarations.concat(ts.optionDeclarations);
for (const option of optionDeclarations) {
optionsIndex[option.name.toLowerCase()] = option;
optionsIndex.set(option.name.toLowerCase(), option);
}
}
return optionsIndex[name.toLowerCase()];
return optionsIndex.get(name.toLowerCase());
}
export function setCompilerOptionsFromHarnessSetting(settings: Harness.TestCaseParser.CompilerSettings, options: ts.CompilerOptions & HarnessOptions): void {
@@ -1480,7 +1481,7 @@ namespace Harness {
const fullResults = ts.createMap<TypeWriterResult[]>();
for (const sourceFile of allFiles) {
fullResults[sourceFile.unitName] = fullWalker.getTypeAndSymbols(sourceFile.unitName);
fullResults.set(sourceFile.unitName, fullWalker.getTypeAndSymbols(sourceFile.unitName));
}
// Produce baselines. The first gives the types for all expressions.
@@ -1533,7 +1534,7 @@ namespace Harness {
allFiles.forEach(file => {
const codeLines = file.content.split("\n");
typeWriterResults[file.unitName].forEach(result => {
typeWriterResults.get(file.unitName).forEach(result => {
if (isSymbolBaseline && !result.symbol) {
return;
}
+2 -2
View File
@@ -262,7 +262,7 @@ namespace Harness.LanguageService {
this.getModuleResolutionsForFile = (fileName) => {
const scriptInfo = this.getScriptInfo(fileName);
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ true);
const imports = ts.createMap<string>();
const imports: ts.MapLike<string> = {};
for (const module of preprocessInfo.importedFiles) {
const resolutionInfo = ts.resolveModuleName(module.fileName, fileName, compilerOptions, moduleResolutionHost);
if (resolutionInfo.resolvedModule) {
@@ -275,7 +275,7 @@ namespace Harness.LanguageService {
const scriptInfo = this.getScriptInfo(fileName);
if (scriptInfo) {
const preprocessInfo = ts.preProcessFile(scriptInfo.content, /*readImportFiles*/ false);
const resolutions = ts.createMap<ts.ResolvedTypeReferenceDirective>();
const resolutions: ts.MapLike<ts.ResolvedTypeReferenceDirective> = {};
const settings = this.nativeHost.getCompilationSettings();
for (const typeReferenceDirective of preprocessInfo.typeReferenceDirectives) {
const resolutionInfo = ts.resolveTypeReferenceDirective(typeReferenceDirective.fileName, fileName, settings, moduleResolutionHost);
+12 -9
View File
@@ -256,17 +256,20 @@ class ProjectRunner extends RunnerBase {
// Set the values specified using json
const optionNameMap = ts.arrayToMap(ts.optionDeclarations, option => option.name);
for (const name in testCase) {
if (name !== "mapRoot" && name !== "sourceRoot" && name in optionNameMap) {
const option = optionNameMap[name];
const optType = option.type;
let value = <any>testCase[name];
if (typeof optType !== "string") {
const key = value.toLowerCase();
if (key in optType) {
value = optType[key];
if (name !== "mapRoot" && name !== "sourceRoot") {
const option = optionNameMap.get(name);
if (option) {
const optType = option.type;
let value = <any>testCase[name];
if (typeof optType !== "string") {
const key = value.toLowerCase();
const optTypeValue = optType.get(key);
if (optTypeValue) {
value = optTypeValue;
}
}
compilerOptions[option.name] = value;
}
compilerOptions[option.name] = value;
}
}
+13 -10
View File
@@ -1,4 +1,4 @@
/// <reference path="..\harness.ts" />
/// <reference path="..\harness.ts" />
namespace ts {
interface File {
@@ -8,25 +8,28 @@ namespace ts {
function createDefaultServerHost(fileMap: Map<File>): server.ServerHost {
const existingDirectories = createMap<boolean>();
for (const name in fileMap) {
forEachKey(fileMap, name => {
let dir = getDirectoryPath(name);
let previous: string;
do {
existingDirectories[dir] = true;
existingDirectories.set(dir, true);
previous = dir;
dir = getDirectoryPath(dir);
} while (dir !== previous);
}
});
return {
args: <string[]>[],
newLine: "\r\n",
useCaseSensitiveFileNames: false,
write: noop,
readFile: path => path in fileMap ? fileMap[path].content : undefined,
readFile: path => {
const file = fileMap.get(path);
return file && file.content;
},
writeFile: notImplemented,
resolvePath: notImplemented,
fileExists: path => path in fileMap,
directoryExists: path => existingDirectories[path] || false,
fileExists: path => fileMap.has(path),
directoryExists: path => existingDirectories.get(path) || false,
createDirectory: noop,
getExecutingFilePath: () => "",
getCurrentDirectory: () => "",
@@ -83,7 +86,7 @@ namespace ts {
content: `foo()`
};
const serverHost = createDefaultServerHost(createMap({ [root.name]: root, [imported.name]: imported }));
const serverHost = createDefaultServerHost(createMapFromTemplate({ [root.name]: root, [imported.name]: imported }));
const { project, rootScriptInfo } = createProject(root.name, serverHost);
// ensure that imported file was found
@@ -167,7 +170,7 @@ namespace ts {
content: `export var y = 1`
};
const fileMap = createMap({ [root.name]: root });
const fileMap = createMapFromTemplate({ [root.name]: root });
const serverHost = createDefaultServerHost(fileMap);
const originalFileExists = serverHost.fileExists;
@@ -191,7 +194,7 @@ namespace ts {
assert.isTrue(typeof diags[0].messageText === "string" && ((<string>diags[0].messageText).indexOf("Cannot find module") === 0), "should be 'cannot find module' message");
// assert that import will success once file appear on disk
fileMap[imported.name] = imported;
fileMap.set(imported.name, imported);
fileExistsCalledForBar = false;
rootScriptInfo.editContent(0, root.content.length, `import {y} from "bar"`);
@@ -2,7 +2,7 @@
/// <reference path="..\virtualFileSystem.ts" />
namespace ts {
const testContents = {
const testContents = createMapFromTemplate({
"/dev/tsconfig.json": `{
"extends": "./configs/base",
"files": [
@@ -86,10 +86,10 @@ namespace ts {
"/dev/tests/utils.ts": "",
"/dev/tests/scenarios/first.json": "",
"/dev/tests/baselines/first/output.ts": ""
};
});
const caseInsensitiveBasePath = "c:/dev/";
const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapObject(testContents, (key, content) => [`c:${key}`, content]));
const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, mapEntries(testContents, (key, content) => [`c:${key}`, content]));
const caseSensitiveBasePath = "/dev/";
const caseSensitiveHost = new Utils.MockParseConfigHost(caseSensitiveBasePath, /*useCaseSensitiveFileNames*/ true, testContents);
+35 -27
View File
@@ -36,7 +36,7 @@ namespace ts {
for (const f of files) {
let name = getDirectoryPath(f.name);
while (true) {
directories[name] = name;
directories.set(name, name);
const baseName = getDirectoryPath(name);
if (baseName === name) {
break;
@@ -46,20 +46,19 @@ namespace ts {
}
return {
readFile,
directoryExists: path => {
return path in directories;
},
directoryExists: path => directories.has(path),
fileExists: path => {
assert.isTrue(getDirectoryPath(path) in directories, `'fileExists' '${path}' request in non-existing directory`);
return path in map;
assert.isTrue(directories.has(getDirectoryPath(path)), `'fileExists' '${path}' request in non-existing directory`);
return map.has(path);
}
};
}
else {
return { readFile, fileExists: path => path in map, };
return { readFile, fileExists: path => map.has(path) };
}
function readFile(path: string): string {
return path in map ? map[path].content : undefined;
const file = map.get(path);
return file && file.content;
}
}
@@ -300,7 +299,8 @@ namespace ts {
const host: CompilerHost = {
getSourceFile: (fileName: string, languageVersion: ScriptTarget) => {
const path = normalizePath(combinePaths(currentDirectory, fileName));
return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined;
const file = files.get(path);
return file && createSourceFile(fileName, file, languageVersion);
},
getDefaultLibFileName: () => "lib.d.ts",
writeFile: notImplemented,
@@ -311,7 +311,7 @@ namespace ts {
useCaseSensitiveFileNames: () => false,
fileExists: fileName => {
const path = normalizePath(combinePaths(currentDirectory, fileName));
return path in files;
return files.has(path);
},
readFile: notImplemented
};
@@ -331,7 +331,7 @@ namespace ts {
}
it("should find all modules", () => {
const files = createMap({
const files = createMapFromTemplate({
"/a/b/c/first/shared.ts": `
class A {}
export = A`,
@@ -350,7 +350,7 @@ export = C;
});
it("should find modules in node_modules", () => {
const files = createMap({
const files = createMapFromTemplate({
"/parent/node_modules/mod/index.d.ts": "export var x",
"/parent/app/myapp.ts": `import {x} from "mod"`
});
@@ -358,7 +358,7 @@ export = C;
});
it("should find file referenced via absolute and relative names", () => {
const files = createMap({
const files = createMapFromTemplate({
"/a/b/c.ts": `/// <reference path="b.ts"/>`,
"/a/b/b.ts": "var x"
});
@@ -371,7 +371,11 @@ export = C;
function test(files: Map<string>, options: CompilerOptions, currentDirectory: string, useCaseSensitiveFileNames: boolean, rootFiles: string[], diagnosticCodes: number[]): void {
const getCanonicalFileName = createGetCanonicalFileName(useCaseSensitiveFileNames);
if (!useCaseSensitiveFileNames) {
files = reduceProperties(files, (files, file, fileName) => (files[getCanonicalFileName(fileName)] = file, files), createMap<string>());
const oldFiles = files;
files = createMap<string>();
oldFiles.forEach((file, fileName) => {
files.set(getCanonicalFileName(fileName), file);
});
}
const host: CompilerHost = {
@@ -380,7 +384,8 @@ export = C;
return library;
}
const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined;
const file = files.get(path);
return file && createSourceFile(fileName, file, languageVersion);
},
getDefaultLibFileName: () => "lib.d.ts",
writeFile: notImplemented,
@@ -391,7 +396,7 @@ export = C;
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
fileExists: fileName => {
const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
return path in files;
return files.has(path);
},
readFile: notImplemented
};
@@ -404,7 +409,7 @@ export = C;
}
it("should succeed when the same file is referenced using absolute and relative names", () => {
const files = createMap({
const files = createMapFromTemplate({
"/a/b/c.ts": `/// <reference path="d.ts"/>`,
"/a/b/d.ts": "var x"
});
@@ -412,7 +417,7 @@ export = C;
});
it("should fail when two files used in program differ only in casing (tripleslash references)", () => {
const files = createMap({
const files = createMapFromTemplate({
"/a/b/c.ts": `/// <reference path="D.ts"/>`,
"/a/b/d.ts": "var x"
});
@@ -420,7 +425,7 @@ export = C;
});
it("should fail when two files used in program differ only in casing (imports)", () => {
const files = createMap({
const files = createMapFromTemplate({
"/a/b/c.ts": `import {x} from "D"`,
"/a/b/d.ts": "export var x"
});
@@ -428,7 +433,7 @@ export = C;
});
it("should fail when two files used in program differ only in casing (imports, relative module names)", () => {
const files = createMap({
const files = createMapFromTemplate({
"moduleA.ts": `import {x} from "./ModuleB"`,
"moduleB.ts": "export var x"
});
@@ -436,7 +441,7 @@ export = C;
});
it("should fail when two files exist on disk that differs only in casing", () => {
const files = createMap({
const files = createMapFromTemplate({
"/a/b/c.ts": `import {x} from "D"`,
"/a/b/D.ts": "export var x",
"/a/b/d.ts": "export var y"
@@ -445,7 +450,7 @@ export = C;
});
it("should fail when module name in 'require' calls has inconsistent casing", () => {
const files = createMap({
const files = createMapFromTemplate({
"moduleA.ts": `import a = require("./ModuleC")`,
"moduleB.ts": `import a = require("./moduleC")`,
"moduleC.ts": "export var x"
@@ -454,7 +459,7 @@ export = C;
});
it("should fail when module names in 'require' calls has inconsistent casing and current directory has uppercase chars", () => {
const files = createMap({
const files = createMapFromTemplate({
"/a/B/c/moduleA.ts": `import a = require("./ModuleC")`,
"/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
"/a/B/c/moduleC.ts": "export var x",
@@ -466,7 +471,7 @@ import b = require("./moduleB");
test(files, { module: ts.ModuleKind.CommonJS, forceConsistentCasingInFileNames: true }, "/a/B/c", /*useCaseSensitiveFileNames*/ false, ["moduleD.ts"], [1149]);
});
it("should not fail when module names in 'require' calls has consistent casing and current directory has uppercase chars", () => {
const files = createMap({
const files = createMapFromTemplate({
"/a/B/c/moduleA.ts": `import a = require("./moduleC")`,
"/a/B/c/moduleB.ts": `import a = require("./moduleC")`,
"/a/B/c/moduleC.ts": "export var x",
@@ -1020,8 +1025,8 @@ import b = require("./moduleB");
const names = map(files, f => f.name);
const sourceFiles = arrayToMap(map(files, f => createSourceFile(f.name, f.content, ScriptTarget.ES2015)), f => f.fileName);
const compilerHost: CompilerHost = {
fileExists : fileName => fileName in sourceFiles,
getSourceFile: fileName => sourceFiles[fileName],
fileExists : fileName => sourceFiles.has(fileName),
getSourceFile: fileName => sourceFiles.get(fileName),
getDefaultLibFileName: () => "lib.d.ts",
writeFile: notImplemented,
getCurrentDirectory: () => "/",
@@ -1029,7 +1034,10 @@ import b = require("./moduleB");
getCanonicalFileName: f => f.toLowerCase(),
getNewLine: () => "\r\n",
useCaseSensitiveFileNames: () => false,
readFile: fileName => fileName in sourceFiles ? sourceFiles[fileName].text : undefined
readFile: fileName => {
const file = sourceFiles.get(fileName);
return file && file.text;
}
};
const program1 = createProgram(names, {}, compilerHost);
const diagnostics1 = program1.getFileProcessingDiagnostics().getDiagnostics();
+26 -11
View File
@@ -1,4 +1,4 @@
/// <reference path="..\harness.ts" />
/// <reference path="..\harness.ts" />
/// <reference path="..\..\harness\harnessLanguageService.ts" />
namespace ts {
@@ -122,7 +122,7 @@ namespace ts {
trace: s => trace.push(s),
getTrace: () => trace,
getSourceFile(fileName): SourceFile {
return files[fileName];
return files.get(fileName);
},
getDefaultLibFileName(): string {
return "lib.d.ts";
@@ -143,9 +143,10 @@ namespace ts {
getNewLine(): string {
return sys ? sys.newLine : newLine;
},
fileExists: fileName => fileName in files,
fileExists: fileName => files.has(fileName),
readFile: fileName => {
return fileName in files ? files[fileName].text : undefined;
const file = files.get(fileName);
return file && file.text;
},
};
}
@@ -188,10 +189,24 @@ namespace ts {
}
else {
assert.isTrue(cache !== undefined, `expected ${caption} to be set`);
assert.isTrue(equalOwnProperties(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`);
assert.isTrue(mapsAreEqual(expectedContent, cache, entryChecker), `contents of ${caption} did not match the expected contents.`);
}
}
/** True if the maps have the same keys and values. */
function mapsAreEqual<T>(left: Map<T>, right: Map<T>, valuesAreEqual?: (left: T, right: T) => boolean): boolean {
if (left === right) return true;
if (!left || !right) return false;
const someInLeftHasNoMatch = forEachEntry(left, (leftValue, leftKey) => {
if (!right.has(leftKey)) return true;
const rightValue = right.get(leftKey);
return !(valuesAreEqual ? valuesAreEqual(leftValue, rightValue) : leftValue === rightValue);
});
if (someInLeftHasNoMatch) return false;
const someInRightHasNoMatch = forEachKey(right, rightKey => !left.has(rightKey));
return !someInRightHasNoMatch;
}
function checkResolvedModulesCache(program: Program, fileName: string, expectedContent: Map<ResolvedModule>): void {
checkCache("resolved modules", program, fileName, expectedContent, f => f.resolvedModules, checkResolvedModule);
}
@@ -307,7 +322,7 @@ namespace ts {
const options: CompilerOptions = { target };
const program_1 = newProgram(files, ["a.ts"], options);
checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": createResolvedModule("b.ts") }));
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
checkResolvedModulesCache(program_1, "b.ts", undefined);
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
@@ -316,7 +331,7 @@ namespace ts {
assert.isTrue(program_1.structureIsReused);
// content of resolution cache should not change
checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": createResolvedModule("b.ts") }));
checkResolvedModulesCache(program_1, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts") }));
checkResolvedModulesCache(program_1, "b.ts", undefined);
// imports has changed - program is not reused
@@ -333,7 +348,7 @@ namespace ts {
files[0].text = files[0].text.updateImportsAndExports(newImports);
});
assert.isTrue(!program_3.structureIsReused);
checkResolvedModulesCache(program_4, "a.ts", createMap({ "b": createResolvedModule("b.ts"), "c": undefined }));
checkResolvedModulesCache(program_4, "a.ts", createMapFromTemplate({ "b": createResolvedModule("b.ts"), "c": undefined }));
});
it("resolved type directives cache follows type directives", () => {
@@ -344,7 +359,7 @@ namespace ts {
const options: CompilerOptions = { target, typeRoots: ["/types"] };
const program_1 = newProgram(files, ["/a.ts"], options);
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined);
const program_2 = updateProgram(program_1, ["/a.ts"], options, files => {
@@ -353,7 +368,7 @@ namespace ts {
assert.isTrue(program_1.structureIsReused);
// content of resolution cache should not change
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
checkResolvedTypeDirectivesCache(program_1, "/types/typedefs/index.d.ts", undefined);
// type reference directives has changed - program is not reused
@@ -371,7 +386,7 @@ namespace ts {
files[0].text = files[0].text.updateReferences(newReferences);
});
assert.isTrue(!program_3.structureIsReused);
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMapFromTemplate({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
});
it("can reuse ambient module declarations from non-modified files", () => {
+9 -7
View File
@@ -1,4 +1,4 @@
/// <reference path="..\harness.ts" />
/// <reference path="..\harness.ts" />
const expect: typeof _chai.expect = _chai.expect;
@@ -416,14 +416,15 @@ namespace ts.server {
class InProcClient {
private server: InProcSession;
private seq = 0;
private callbacks = createMap<(resp: protocol.Response) => void>();
private callbacks: Array<(resp: protocol.Response) => void> = [];
private eventHandlers = createMap<(args: any) => void>();
handle(msg: protocol.Message): void {
if (msg.type === "response") {
const response = <protocol.Response>msg;
if (response.request_seq in this.callbacks) {
this.callbacks[response.request_seq](response);
const handler = this.callbacks[response.request_seq];
if (handler) {
handler(response);
delete this.callbacks[response.request_seq];
}
}
@@ -434,13 +435,14 @@ namespace ts.server {
}
emit(name: string, args: any): void {
if (name in this.eventHandlers) {
this.eventHandlers[name](args);
const handler = this.eventHandlers.get(name);
if (handler) {
handler(args);
}
}
on(name: string, handler: (args: any) => void): void {
this.eventHandlers[name] = handler;
this.eventHandlers.set(name, handler);
}
connect(session: InProcSession): void {
+15 -19
View File
@@ -1,4 +1,4 @@
/// <reference path="..\harness.ts" />
/// <reference path="..\harness.ts" />
/// <reference path="../../server/typingsInstaller/typingsInstaller.ts" />
namespace ts.projectSystem {
@@ -244,9 +244,9 @@ namespace ts.projectSystem {
}
export function checkMapKeys(caption: string, map: Map<any>, expectedKeys: string[]) {
assert.equal(reduceProperties(map, count => count + 1, 0), expectedKeys.length, `${caption}: incorrect size of map`);
assert.equal(map.size, expectedKeys.length, `${caption}: incorrect size of map`);
for (const name of expectedKeys) {
assert.isTrue(name in map, `${caption} is expected to contain ${name}, actual keys: ${Object.keys(map)}`);
assert.isTrue(map.has(name), `${caption} is expected to contain ${name}, actual keys: ${arrayFrom(map.keys())}`);
}
}
@@ -292,7 +292,7 @@ namespace ts.projectSystem {
}
export class Callbacks {
private map: { [n: number]: TimeOutCallback } = {};
private map: TimeOutCallback[] = [];
private nextId = 1;
register(cb: (...args: any[]) => void, args: any[]) {
@@ -310,20 +310,16 @@ namespace ts.projectSystem {
count() {
let n = 0;
for (const _ in this.map) {
// TODO: GH#11734
_;
n++;
}
return n;
}
invoke() {
for (const id in this.map) {
if (hasProperty(this.map, id)) {
this.map[id]();
}
for (const key in this.map) {
this.map[key]();
}
this.map = {};
this.map = [];
}
}
@@ -338,8 +334,8 @@ namespace ts.projectSystem {
private timeoutCallbacks = new Callbacks();
private immediateCallbacks = new Callbacks();
readonly watchedDirectories = createMap<{ cb: DirectoryWatcherCallback, recursive: boolean }[]>();
readonly watchedFiles = createMap<FileWatcherCallback[]>();
readonly watchedDirectories = createMultiMap<{ cb: DirectoryWatcherCallback, recursive: boolean }>();
readonly watchedFiles = createMultiMap<FileWatcherCallback>();
private filesOrFolders: FileOrFolder[];
@@ -425,11 +421,11 @@ namespace ts.projectSystem {
watchDirectory(directoryName: string, callback: DirectoryWatcherCallback, recursive: boolean): DirectoryWatcher {
const path = this.toPath(directoryName);
const cbWithRecursive = { cb: callback, recursive };
multiMapAdd(this.watchedDirectories, path, cbWithRecursive);
this.watchedDirectories.add(path, cbWithRecursive);
return {
referenceCount: 0,
directoryName,
close: () => multiMapRemove(this.watchedDirectories, path, cbWithRecursive)
close: () => this.watchedDirectories.remove(path, cbWithRecursive)
};
}
@@ -439,7 +435,7 @@ namespace ts.projectSystem {
triggerDirectoryWatcherCallback(directoryName: string, fileName: string): void {
const path = this.toPath(directoryName);
const callbacks = this.watchedDirectories[path];
const callbacks = this.watchedDirectories.get(path);
if (callbacks) {
for (const callback of callbacks) {
callback.cb(fileName);
@@ -449,7 +445,7 @@ namespace ts.projectSystem {
triggerFileWatcherCallback(fileName: string, removed?: boolean): void {
const path = this.toPath(fileName);
const callbacks = this.watchedFiles[path];
const callbacks = this.watchedFiles.get(path);
if (callbacks) {
for (const callback of callbacks) {
callback(path, removed);
@@ -459,8 +455,8 @@ namespace ts.projectSystem {
watchFile(fileName: string, callback: FileWatcherCallback) {
const path = this.toPath(fileName);
multiMapAdd(this.watchedFiles, path, callback);
return { close: () => multiMapRemove(this.watchedFiles, path, callback) };
this.watchedFiles.add(path, callback);
return { close: () => this.watchedFiles.remove(path, callback) };
}
// TOOD: record and invoke callbacks to simulate timer events
+2 -2
View File
@@ -14,7 +14,7 @@ namespace ts.projectSystem {
function createTypesRegistry(...list: string[]): Map<void> {
const map = createMap<void>();
for (const l of list) {
map[l] = undefined;
map.set(l, undefined);
}
return map;
}
@@ -944,7 +944,7 @@ namespace ts.projectSystem {
content: ""
};
const host = createServerHost([f, node]);
const cache = createMap<string>({ "node": node.path });
const cache = createMapFromTemplate<string>({ "node": node.path });
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, ["fs", "bar"]);
assert.deepEqual(result.cachedTypingPaths, [node.path]);
assert.deepEqual(result.newTypingNames, ["bar"]);
+10 -4
View File
@@ -195,11 +195,17 @@ namespace Utils {
}
export class MockParseConfigHost extends VirtualFileSystem implements ts.ParseConfigHost {
constructor(currentDirectory: string, ignoreCase: boolean, files: ts.MapLike<string> | string[]) {
constructor(currentDirectory: string, ignoreCase: boolean, files: ts.Map<string> | string[]) {
super(currentDirectory, ignoreCase);
const fileNames = (files instanceof Array) ? files : ts.getOwnKeys(files);
for (const file of fileNames) {
this.addFile(file, new Harness.LanguageService.ScriptInfo(file, (files as ts.MapLike<string>)[file], /*isRootFile*/false));
if (files instanceof Array) {
for (const file of files) {
this.addFile(file, new Harness.LanguageService.ScriptInfo(file, undefined, /*isRootFile*/false));
}
}
else {
files.forEach((fileContent, fileName) => {
this.addFile(fileName, new Harness.LanguageService.ScriptInfo(fileName, fileContent, /*isRootFile*/false));
});
}
}