mirror of
https://github.com/microsoft/TypeScript.git
synced 2025-11-18 17:21:48 +00:00
Merge branch 'master' of https://github.com/Microsoft/TypeScript into 11116
This commit is contained in:
@@ -170,7 +170,7 @@ class CompilerBaselineRunner extends RunnerBase {
|
||||
});
|
||||
|
||||
it("Correct Sourcemap output for " + fileName, () => {
|
||||
Harness.Compiler.doSourcemapBaseline(justName, options, result);
|
||||
Harness.Compiler.doSourcemapBaseline(justName, options, result, harnessSettings);
|
||||
});
|
||||
|
||||
it("Correct type/symbol baselines for " + fileName, () => {
|
||||
|
||||
+132
-201
@@ -47,19 +47,6 @@ namespace FourSlash {
|
||||
ranges: Range[];
|
||||
}
|
||||
|
||||
interface MemberListData {
|
||||
result: {
|
||||
maybeInaccurate: boolean;
|
||||
isMemberCompletion: boolean;
|
||||
entries: {
|
||||
name: string;
|
||||
type: string;
|
||||
kind: string;
|
||||
kindModifiers: string;
|
||||
}[];
|
||||
};
|
||||
}
|
||||
|
||||
export interface Marker {
|
||||
fileName: string;
|
||||
position: number;
|
||||
@@ -178,15 +165,9 @@ namespace FourSlash {
|
||||
// Return object may lack some functionalities for other purposes.
|
||||
function createScriptSnapShot(sourceText: string): ts.IScriptSnapshot {
|
||||
return {
|
||||
getText: (start: number, end: number) => {
|
||||
return sourceText.substr(start, end - start);
|
||||
},
|
||||
getLength: () => {
|
||||
return sourceText.length;
|
||||
},
|
||||
getChangeRange: (oldSnapshot: ts.IScriptSnapshot) => {
|
||||
return <ts.TextChangeRange>undefined;
|
||||
}
|
||||
getText: (start: number, end: number) => sourceText.substr(start, end - start),
|
||||
getLength: () => sourceText.length,
|
||||
getChangeRange: () => undefined
|
||||
};
|
||||
}
|
||||
|
||||
@@ -360,6 +341,7 @@ namespace FourSlash {
|
||||
insertSpaceAfterCommaDelimiter: true,
|
||||
insertSpaceAfterSemicolonInForStatements: true,
|
||||
insertSpaceBeforeAndAfterBinaryOperators: true,
|
||||
insertSpaceAfterConstructor: false,
|
||||
insertSpaceAfterKeywordsInControlFlowStatements: true,
|
||||
insertSpaceAfterFunctionKeywordForAnonymousFunctions: false,
|
||||
insertSpaceAfterOpeningAndBeforeClosingNonemptyParenthesis: false,
|
||||
@@ -419,9 +401,8 @@ namespace FourSlash {
|
||||
public verifyErrorExistsBetweenMarkers(startMarkerName: string, endMarkerName: string, negative: boolean) {
|
||||
const startMarker = this.getMarkerByName(startMarkerName);
|
||||
const endMarker = this.getMarkerByName(endMarkerName);
|
||||
const predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) {
|
||||
return ((errorMinChar === startPos) && (errorLimChar === endPos)) ? true : false;
|
||||
};
|
||||
const predicate = (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) =>
|
||||
((errorMinChar === startPos) && (errorLimChar === endPos)) ? true : false;
|
||||
|
||||
const exists = this.anyErrorInRange(predicate, startMarker, endMarker);
|
||||
|
||||
@@ -458,9 +439,8 @@ namespace FourSlash {
|
||||
private getAllDiagnostics(): ts.Diagnostic[] {
|
||||
const diagnostics: ts.Diagnostic[] = [];
|
||||
|
||||
const fileNames = this.languageServiceAdapterHost.getFilenames();
|
||||
for (let i = 0, n = fileNames.length; i < n; i++) {
|
||||
diagnostics.push.apply(this.getDiagnostics(fileNames[i]));
|
||||
for (const fileName of this.languageServiceAdapterHost.getFilenames()) {
|
||||
diagnostics.push.apply(this.getDiagnostics(fileName));
|
||||
}
|
||||
|
||||
return diagnostics;
|
||||
@@ -471,14 +451,12 @@ namespace FourSlash {
|
||||
let predicate: (errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) => boolean;
|
||||
|
||||
if (after) {
|
||||
predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) {
|
||||
return ((errorMinChar >= startPos) && (errorLimChar >= startPos)) ? true : false;
|
||||
};
|
||||
predicate = (errorMinChar: number, errorLimChar: number, startPos: number) =>
|
||||
((errorMinChar >= startPos) && (errorLimChar >= startPos)) ? true : false;
|
||||
}
|
||||
else {
|
||||
predicate = function(errorMinChar: number, errorLimChar: number, startPos: number, endPos: number) {
|
||||
return ((errorMinChar <= startPos) && (errorLimChar <= startPos)) ? true : false;
|
||||
};
|
||||
predicate = (errorMinChar: number, errorLimChar: number, startPos: number) =>
|
||||
((errorMinChar <= startPos) && (errorLimChar <= startPos)) ? true : false;
|
||||
}
|
||||
|
||||
const exists = this.anyErrorInRange(predicate, marker);
|
||||
@@ -501,7 +479,7 @@ namespace FourSlash {
|
||||
endPos = endMarker.position;
|
||||
}
|
||||
|
||||
errors.forEach(function(error: ts.Diagnostic) {
|
||||
errors.forEach(function (error: ts.Diagnostic) {
|
||||
if (predicate(error.start, error.start + error.length, startPos, endPos)) {
|
||||
exists = true;
|
||||
}
|
||||
@@ -518,7 +496,7 @@ namespace FourSlash {
|
||||
Harness.IO.log("Unexpected error(s) found. Error list is:");
|
||||
}
|
||||
|
||||
errors.forEach(function(error: ts.Diagnostic) {
|
||||
errors.forEach(function (error: ts.Diagnostic) {
|
||||
Harness.IO.log(" minChar: " + error.start +
|
||||
", limChar: " + (error.start + error.length) +
|
||||
", message: " + ts.flattenDiagnosticMessageText(error.messageText, Harness.IO.newLine()) + "\n");
|
||||
@@ -602,12 +580,12 @@ namespace FourSlash {
|
||||
this.raiseError(`goToDefinitions failed - expected to find ${endMarkers.length} definitions but got ${definitions.length}`);
|
||||
}
|
||||
|
||||
for (let i = 0; i < endMarkers.length; i++) {
|
||||
const marker = this.getMarkerByName(endMarkers[i]), definition = definitions[i];
|
||||
ts.zipWith(endMarkers, definitions, (endMarker, definition, i) => {
|
||||
const marker = this.getMarkerByName(endMarker);
|
||||
if (marker.fileName !== definition.fileName || marker.position !== definition.textSpan.start) {
|
||||
this.raiseError(`goToDefinition failed for definition ${i}: expected ${marker.fileName} at ${marker.position}, got ${definition.fileName} at ${definition.textSpan.start}`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public verifyGetEmitOutputForCurrentFile(expected: string): void {
|
||||
@@ -624,29 +602,19 @@ namespace FourSlash {
|
||||
public verifyGetEmitOutputContentsForCurrentFile(expected: ts.OutputFile[]): void {
|
||||
const emit = this.languageService.getEmitOutput(this.activeFile.fileName);
|
||||
assert.equal(emit.outputFiles.length, expected.length, "Number of emit output files");
|
||||
for (let i = 0; i < emit.outputFiles.length; i++) {
|
||||
assert.equal(emit.outputFiles[i].name, expected[i].name, "FileName");
|
||||
assert.equal(emit.outputFiles[i].text, expected[i].text, "Content");
|
||||
}
|
||||
ts.zipWith(emit.outputFiles, expected, (outputFile, expected) => {
|
||||
assert.equal(outputFile.name, expected.name, "FileName");
|
||||
assert.equal(outputFile.text, expected.text, "Content");
|
||||
});
|
||||
}
|
||||
|
||||
public verifyMemberListContains(symbol: string, text?: string, documentation?: string, kind?: string) {
|
||||
const members = this.getMemberListAtCaret();
|
||||
if (members) {
|
||||
this.assertItemInCompletionList(members.entries, symbol, text, documentation, kind);
|
||||
}
|
||||
else {
|
||||
this.raiseError("Expected a member list, but none was provided");
|
||||
}
|
||||
}
|
||||
|
||||
public verifyMemberListCount(expectedCount: number, negative: boolean) {
|
||||
public verifyCompletionListCount(expectedCount: number, negative: boolean) {
|
||||
if (expectedCount === 0 && negative) {
|
||||
this.verifyMemberListIsEmpty(/*negative*/ false);
|
||||
this.verifyCompletionListIsEmpty(/*negative*/ false);
|
||||
return;
|
||||
}
|
||||
|
||||
const members = this.getMemberListAtCaret();
|
||||
const members = this.getCompletionListAtCaret();
|
||||
|
||||
if (members) {
|
||||
const match = members.entries.length === expectedCount;
|
||||
@@ -660,13 +628,6 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyMemberListDoesNotContain(symbol: string) {
|
||||
const members = this.getMemberListAtCaret();
|
||||
if (members && members.entries.filter(e => e.name === symbol).length !== 0) {
|
||||
this.raiseError(`Member list did contain ${symbol}`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListItemsCountIsGreaterThan(count: number, negative: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
const itemsCount = completions.entries.length;
|
||||
@@ -690,9 +651,9 @@ namespace FourSlash {
|
||||
|
||||
const entries = this.getCompletionListAtCaret().entries;
|
||||
assert.isTrue(items.length <= entries.length, `Amount of expected items in completion list [ ${items.length} ] is greater than actual number of items in list [ ${entries.length} ]`);
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
assert.equal(entries[i].name, items[i], `Unexpected item in completion list`);
|
||||
}
|
||||
ts.zipWith(entries, items, (entry, item) => {
|
||||
assert.equal(entry.name, item, `Unexpected item in completion list`);
|
||||
});
|
||||
}
|
||||
|
||||
public noItemsWithSameNameButDifferentKind(): void {
|
||||
@@ -708,37 +669,14 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyMemberListIsEmpty(negative: boolean) {
|
||||
const members = this.getMemberListAtCaret();
|
||||
if ((!members || members.entries.length === 0) && negative) {
|
||||
this.raiseError("Member list is empty at Caret");
|
||||
}
|
||||
else if ((members && members.entries.length !== 0) && !negative) {
|
||||
|
||||
let errorMsg = "\n" + "Member List contains: [" + members.entries[0].name;
|
||||
for (let i = 1; i < members.entries.length; i++) {
|
||||
errorMsg += ", " + members.entries[i].name;
|
||||
}
|
||||
errorMsg += "]\n";
|
||||
|
||||
this.raiseError("Member list is not empty at Caret: " + errorMsg);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
public verifyCompletionListIsEmpty(negative: boolean) {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
if ((!completions || completions.entries.length === 0) && negative) {
|
||||
this.raiseError("Completion list is empty at caret at position " + this.activeFile.fileName + " " + this.currentCaretPosition);
|
||||
}
|
||||
else if (completions && completions.entries.length !== 0 && !negative) {
|
||||
let errorMsg = "\n" + "Completion List contains: [" + completions.entries[0].name;
|
||||
for (let i = 1; i < completions.entries.length; i++) {
|
||||
errorMsg += ", " + completions.entries[i].name;
|
||||
}
|
||||
errorMsg += "]\n";
|
||||
|
||||
this.raiseError("Completion list is not empty at caret at position " + this.activeFile.fileName + " " + this.currentCaretPosition + errorMsg);
|
||||
this.raiseError(`Completion list is not empty at caret at position ${this.activeFile.fileName} ${this.currentCaretPosition}\n` +
|
||||
`Completion List contains: ${stringify(completions.entries.map(e => e.name))}`);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -912,8 +850,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
private verifyReferencesWorker(references: ts.ReferenceEntry[], fileName: string, start: number, end: number, isWriteAccess?: boolean, isDefinition?: boolean) {
|
||||
for (let i = 0; i < references.length; i++) {
|
||||
const reference = references[i];
|
||||
for (const reference of references) {
|
||||
if (reference && reference.fileName === fileName && reference.textSpan.start === start && ts.textSpanEnd(reference.textSpan) === end) {
|
||||
if (typeof isWriteAccess !== "undefined" && reference.isWriteAccess !== isWriteAccess) {
|
||||
this.raiseError(`verifyReferencesAtPositionListContains failed - item isWriteAccess value does not match, actual: ${reference.isWriteAccess}, expected: ${isWriteAccess}.`);
|
||||
@@ -929,10 +866,6 @@ namespace FourSlash {
|
||||
this.raiseError(`verifyReferencesAtPositionListContains failed - could not find the item: ${stringify(missingItem)} in the returned list: (${stringify(references)})`);
|
||||
}
|
||||
|
||||
private getMemberListAtCaret() {
|
||||
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
}
|
||||
|
||||
private getCompletionListAtCaret() {
|
||||
return this.languageService.getCompletionsAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
}
|
||||
@@ -1030,16 +963,11 @@ namespace FourSlash {
|
||||
ranges = ranges.sort((r1, r2) => r1.start - r2.start);
|
||||
references = references.sort((r1, r2) => r1.textSpan.start - r2.textSpan.start);
|
||||
|
||||
for (let i = 0, n = ranges.length; i < n; i++) {
|
||||
const reference = references[i];
|
||||
const range = ranges[i];
|
||||
|
||||
if (reference.textSpan.start !== range.start ||
|
||||
ts.textSpanEnd(reference.textSpan) !== range.end) {
|
||||
|
||||
ts.zipWith(references, ranges, (reference, range) => {
|
||||
if (reference.textSpan.start !== range.start || ts.textSpanEnd(reference.textSpan) !== range.end) {
|
||||
this.raiseError("Rename location results do not match.\n\nExpected: " + stringify(ranges) + "\n\nActual:" + JSON.stringify(references));
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
else {
|
||||
this.raiseError("Expected rename to succeed, but it actually failed.");
|
||||
@@ -1068,7 +996,7 @@ namespace FourSlash {
|
||||
ts.displayPartsToString(help.suffixDisplayParts), expected);
|
||||
}
|
||||
|
||||
public verifyCurrentParameterIsletiable(isVariable: boolean) {
|
||||
public verifyCurrentParameterIsVariable(isVariable: boolean) {
|
||||
const signature = this.getActiveSignatureHelpItem();
|
||||
assert.isOk(signature);
|
||||
assert.equal(isVariable, signature.isVariadic);
|
||||
@@ -1095,6 +1023,10 @@ namespace FourSlash {
|
||||
assert.equal(this.getActiveSignatureHelpItem().parameters.length, expectedCount);
|
||||
}
|
||||
|
||||
public verifyCurrentSignatureHelpIsVariadic(expected: boolean) {
|
||||
assert.equal(this.getActiveSignatureHelpItem().isVariadic, expected);
|
||||
}
|
||||
|
||||
public verifyCurrentSignatureHelpDocComment(docComment: string) {
|
||||
const actualDocComment = this.getActiveSignatureHelpItem().documentation;
|
||||
assert.equal(ts.displayPartsToString(actualDocComment), docComment, this.assertionMessageAtLastKnownMarker("current signature help doc comment"));
|
||||
@@ -1179,7 +1111,7 @@ namespace FourSlash {
|
||||
|
||||
private alignmentForExtraInfo = 50;
|
||||
|
||||
private spanInfoToString(pos: number, spanInfo: ts.TextSpan, prefixString: string) {
|
||||
private spanInfoToString(spanInfo: ts.TextSpan, prefixString: string) {
|
||||
let resultString = "SpanInfo: " + JSON.stringify(spanInfo);
|
||||
if (spanInfo) {
|
||||
const spanString = this.activeFile.content.substr(spanInfo.start, spanInfo.length);
|
||||
@@ -1230,7 +1162,7 @@ namespace FourSlash {
|
||||
startColumn = 0;
|
||||
length = 0;
|
||||
}
|
||||
const spanInfo = this.spanInfoToString(pos, getSpanAtPos(pos), prefixString);
|
||||
const spanInfo = this.spanInfoToString(getSpanAtPos(pos), prefixString);
|
||||
if (previousSpanInfo && previousSpanInfo !== spanInfo) {
|
||||
addSpanInfoString();
|
||||
previousSpanInfo = spanInfo;
|
||||
@@ -1269,8 +1201,7 @@ namespace FourSlash {
|
||||
const emitFiles: FourSlashFile[] = []; // List of FourSlashFile that has emitThisFile flag on
|
||||
|
||||
const allFourSlashFiles = this.testData.files;
|
||||
for (let idx = 0; idx < allFourSlashFiles.length; idx++) {
|
||||
const file = allFourSlashFiles[idx];
|
||||
for (const file of allFourSlashFiles) {
|
||||
if (file.fileOptions[metadataOptionNames.emitThisFile] === "true") {
|
||||
// Find a file with the flag emitThisFile turned on
|
||||
emitFiles.push(file);
|
||||
@@ -1295,15 +1226,26 @@ namespace FourSlash {
|
||||
if (emitOutput.emitSkipped) {
|
||||
resultString += "Diagnostics:" + Harness.IO.newLine();
|
||||
const diagnostics = ts.getPreEmitDiagnostics(this.languageService.getProgram());
|
||||
for (let i = 0, n = diagnostics.length; i < n; i++) {
|
||||
resultString += " " + diagnostics[0].messageText + Harness.IO.newLine();
|
||||
for (const diagnostic of diagnostics) {
|
||||
if (typeof diagnostic.messageText !== "string") {
|
||||
let chainedMessage = <ts.DiagnosticMessageChain>diagnostic.messageText;
|
||||
let indentation = " ";
|
||||
while (chainedMessage) {
|
||||
resultString += indentation + chainedMessage.messageText + Harness.IO.newLine();
|
||||
chainedMessage = chainedMessage.next;
|
||||
indentation = indentation + " ";
|
||||
}
|
||||
}
|
||||
else {
|
||||
resultString += " " + diagnostic.messageText + Harness.IO.newLine();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
emitOutput.outputFiles.forEach((outputFile, idx, array) => {
|
||||
for (const outputFile of emitOutput.outputFiles) {
|
||||
const fileName = "FileName : " + outputFile.name + Harness.IO.newLine();
|
||||
resultString = resultString + fileName + outputFile.text;
|
||||
});
|
||||
}
|
||||
resultString += Harness.IO.newLine();
|
||||
});
|
||||
|
||||
@@ -1328,7 +1270,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public printBreakpointLocation(pos: number) {
|
||||
Harness.IO.log("\n**Pos: " + pos + " " + this.spanInfoToString(pos, this.getBreakpointStatementLocation(pos), " "));
|
||||
Harness.IO.log("\n**Pos: " + pos + " " + this.spanInfoToString(this.getBreakpointStatementLocation(pos), " "));
|
||||
}
|
||||
|
||||
public printBreakpointAtCurrentLocation() {
|
||||
@@ -1362,8 +1304,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public printCurrentFileState(makeWhitespaceVisible = false, makeCaretVisible = true) {
|
||||
for (let i = 0; i < this.testData.files.length; i++) {
|
||||
const file = this.testData.files[i];
|
||||
for (const file of this.testData.files) {
|
||||
const active = (this.activeFile === file);
|
||||
Harness.IO.log(`=== Script (${file.fileName}) ${(active ? "(active, cursor at |)" : "")} ===`);
|
||||
let content = this.getFileContent(file.fileName);
|
||||
@@ -1382,11 +1323,6 @@ namespace FourSlash {
|
||||
Harness.IO.log(stringify(sigHelp));
|
||||
}
|
||||
|
||||
public printMemberListMembers() {
|
||||
const members = this.getMemberListAtCaret();
|
||||
this.printMembersOrCompletions(members);
|
||||
}
|
||||
|
||||
public printCompletionListMembers() {
|
||||
const completions = this.getCompletionListAtCaret();
|
||||
this.printMembersOrCompletions(completions);
|
||||
@@ -1598,10 +1534,10 @@ namespace FourSlash {
|
||||
edits = edits.sort((a, b) => a.span.start - b.span.start);
|
||||
// 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(this.activeFile.fileName);
|
||||
for (let j = 0; j < edits.length; j++) {
|
||||
this.languageServiceAdapterHost.editScript(fileName, edits[j].span.start + runningOffset, ts.textSpanEnd(edits[j].span) + runningOffset, edits[j].newText);
|
||||
this.updateMarkersForEdit(fileName, edits[j].span.start + runningOffset, ts.textSpanEnd(edits[j].span) + runningOffset, edits[j].newText);
|
||||
const change = (edits[j].span.start - ts.textSpanEnd(edits[j].span)) + edits[j].newText.length;
|
||||
for (const edit of edits) {
|
||||
this.languageServiceAdapterHost.editScript(fileName, edit.span.start + runningOffset, ts.textSpanEnd(edit.span) + runningOffset, edit.newText);
|
||||
this.updateMarkersForEdit(fileName, edit.span.start + runningOffset, ts.textSpanEnd(edit.span) + runningOffset, edit.newText);
|
||||
const change = (edit.span.start - ts.textSpanEnd(edit.span)) + edit.newText.length;
|
||||
runningOffset += change;
|
||||
// TODO: Consider doing this at least some of the time for higher fidelity. Currently causes a failure (bug 707150)
|
||||
// this.languageService.getScriptLexicalStructure(fileName);
|
||||
@@ -1830,7 +1766,7 @@ namespace FourSlash {
|
||||
return result;
|
||||
}
|
||||
|
||||
private rangeText({fileName, start, end}: Range, more = false): string {
|
||||
private rangeText({fileName, start, end}: Range): string {
|
||||
return this.getFileContent(fileName).slice(start, end);
|
||||
}
|
||||
|
||||
@@ -1925,7 +1861,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
public printNameOrDottedNameSpans(pos: number) {
|
||||
Harness.IO.log(this.spanInfoToString(pos, this.getNameOrDottedNameSpan(pos), "**"));
|
||||
Harness.IO.log(this.spanInfoToString(this.getNameOrDottedNameSpan(pos), "**"));
|
||||
}
|
||||
|
||||
private verifyClassifications(expected: { classificationType: string; text: string; textSpan?: TextSpan }[], actual: ts.ClassifiedSpan[]) {
|
||||
@@ -1935,10 +1871,7 @@ namespace FourSlash {
|
||||
jsonMismatchString());
|
||||
}
|
||||
|
||||
for (let i = 0; i < expected.length; i++) {
|
||||
const expectedClassification = expected[i];
|
||||
const actualClassification = actual[i];
|
||||
|
||||
ts.zipWith(expected, actual, (expectedClassification, actualClassification) => {
|
||||
const expectedType: string = (<any>ts.ClassificationTypeNames)[expectedClassification.classificationType];
|
||||
if (expectedType !== actualClassification.classificationType) {
|
||||
this.raiseError("verifyClassifications failed - expected classifications type to be " +
|
||||
@@ -1968,7 +1901,7 @@ namespace FourSlash {
|
||||
actualText +
|
||||
jsonMismatchString());
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
function jsonMismatchString() {
|
||||
return Harness.IO.newLine() +
|
||||
@@ -2013,13 +1946,11 @@ namespace FourSlash {
|
||||
this.raiseError(`verifyOutliningSpans failed - expected total spans to be ${spans.length}, but was ${actual.length}`);
|
||||
}
|
||||
|
||||
for (let i = 0; i < spans.length; i++) {
|
||||
const expectedSpan = spans[i];
|
||||
const actualSpan = actual[i];
|
||||
ts.zipWith(spans, actual, (expectedSpan, actualSpan, i) => {
|
||||
if (expectedSpan.start !== actualSpan.textSpan.start || expectedSpan.end !== ts.textSpanEnd(actualSpan.textSpan)) {
|
||||
this.raiseError(`verifyOutliningSpans failed - span ${(i + 1)} expected: (${expectedSpan.start},${expectedSpan.end}), actual: (${actualSpan.textSpan.start},${ts.textSpanEnd(actualSpan.textSpan)})`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public verifyTodoComments(descriptors: string[], spans: TextSpan[]) {
|
||||
@@ -2030,15 +1961,13 @@ namespace FourSlash {
|
||||
this.raiseError(`verifyTodoComments failed - expected total spans to be ${spans.length}, but was ${actual.length}`);
|
||||
}
|
||||
|
||||
for (let i = 0; i < spans.length; i++) {
|
||||
const expectedSpan = spans[i];
|
||||
const actualComment = actual[i];
|
||||
ts.zipWith(spans, actual, (expectedSpan, actualComment, i) => {
|
||||
const actualCommentSpan = ts.createTextSpan(actualComment.position, actualComment.message.length);
|
||||
|
||||
if (expectedSpan.start !== actualCommentSpan.start || expectedSpan.end !== ts.textSpanEnd(actualCommentSpan)) {
|
||||
this.raiseError(`verifyOutliningSpans failed - span ${(i + 1)} expected: (${expectedSpan.start},${expectedSpan.end}), actual: (${actualCommentSpan.start},${ts.textSpanEnd(actualCommentSpan)})`);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private getCodeFixes(errorCode?: number) {
|
||||
@@ -2049,13 +1978,13 @@ namespace FourSlash {
|
||||
this.raiseError("Errors expected.");
|
||||
}
|
||||
|
||||
if (diagnostics.length > 1 && errorCode !== undefined) {
|
||||
if (diagnostics.length > 1 && errorCode === undefined) {
|
||||
this.raiseError("When there's more than one error, you must specify the errror to fix.");
|
||||
}
|
||||
|
||||
const diagnostic = !errorCode ? diagnostics[0] : ts.find(diagnostics, d => d.code == errorCode);
|
||||
|
||||
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.length, [diagnostic.code]);
|
||||
return this.languageService.getCodeFixesAtPosition(fileName, diagnostic.start, diagnostic.start + diagnostic.length, [diagnostic.code]);
|
||||
}
|
||||
|
||||
public verifyCodeFixAtPosition(expectedText: string, errorCode?: number) {
|
||||
@@ -2082,6 +2011,34 @@ namespace FourSlash {
|
||||
}
|
||||
}
|
||||
|
||||
public verifyImportFixAtPosition(expectedTextArray: string[], errorCode?: number) {
|
||||
const ranges = this.getRanges();
|
||||
if (ranges.length == 0) {
|
||||
this.raiseError("At least one range should be specified in the testfile.");
|
||||
}
|
||||
|
||||
const codeFixes = this.getCodeFixes(errorCode);
|
||||
|
||||
if (!codeFixes || codeFixes.length == 0) {
|
||||
this.raiseError("No codefixes returned.");
|
||||
}
|
||||
|
||||
const actualTextArray: string[] = [];
|
||||
const scriptInfo = this.languageServiceAdapterHost.getScriptInfo(codeFixes[0].changes[0].fileName);
|
||||
const originalContent = scriptInfo.content;
|
||||
for (const codeFix of codeFixes) {
|
||||
this.applyEdits(codeFix.changes[0].fileName, codeFix.changes[0].textChanges, /*isFormattingEdit*/ false);
|
||||
actualTextArray.push(this.normalizeNewlines(this.rangeText(ranges[0])));
|
||||
scriptInfo.updateContent(originalContent);
|
||||
}
|
||||
const sortedExpectedArray = ts.map(expectedTextArray, str => this.normalizeNewlines(str)).sort();
|
||||
const sortedActualArray = actualTextArray.sort();
|
||||
if (!ts.arrayIsEqualTo(sortedExpectedArray, sortedActualArray)) {
|
||||
this.raiseError(
|
||||
`Actual text array doesn't match expected text array. \nActual: \n"${sortedActualArray.join("\n\n")}"\n---\nExpected: \n'${sortedExpectedArray.join("\n\n")}'`);
|
||||
}
|
||||
}
|
||||
|
||||
public verifyDocCommentTemplate(expected?: ts.TextInsertion) {
|
||||
const name = "verifyDocCommentTemplate";
|
||||
const actual = this.languageService.getDocCommentTemplateAtPosition(this.activeFile.fileName, this.currentCaretPosition);
|
||||
@@ -2115,6 +2072,10 @@ namespace FourSlash {
|
||||
});
|
||||
}
|
||||
|
||||
private normalizeNewlines(str: string) {
|
||||
return str.replace(/\r?\n/g, "\n");
|
||||
}
|
||||
|
||||
public verifyBraceCompletionAtPosition(negative: boolean, openingBrace: string) {
|
||||
|
||||
const openBraceMap = ts.createMap<ts.CharacterCodes>({
|
||||
@@ -2185,11 +2146,9 @@ namespace FourSlash {
|
||||
public verifyNavigationItemsCount(expected: number, searchValue: string, matchKind?: string, fileName?: string) {
|
||||
const items = this.languageService.getNavigateToItems(searchValue, /*maxResultCount*/ undefined, fileName);
|
||||
let actual = 0;
|
||||
let item: ts.NavigateToItem;
|
||||
|
||||
// Count only the match that match the same MatchKind
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
item = items[i];
|
||||
for (const item of items) {
|
||||
if (!matchKind || item.matchKind === matchKind) {
|
||||
actual++;
|
||||
}
|
||||
@@ -2217,8 +2176,7 @@ namespace FourSlash {
|
||||
this.raiseError("verifyNavigationItemsListContains failed - found 0 navigation items, expected at least one.");
|
||||
}
|
||||
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
for (const item of items) {
|
||||
if (item && item.name === name && item.kind === kind &&
|
||||
(matchKind === undefined || item.matchKind === matchKind) &&
|
||||
(fileName === undefined || item.fileName === fileName) &&
|
||||
@@ -2269,24 +2227,16 @@ namespace FourSlash {
|
||||
|
||||
public printNavigationItems(searchValue: string) {
|
||||
const items = this.languageService.getNavigateToItems(searchValue);
|
||||
const length = items && items.length;
|
||||
|
||||
Harness.IO.log(`NavigationItems list (${length} items)`);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const item = items[i];
|
||||
Harness.IO.log(`NavigationItems list (${items.length} items)`);
|
||||
for (const item of items) {
|
||||
Harness.IO.log(`name: ${item.name}, kind: ${item.kind}, parentName: ${item.containerName}, fileName: ${item.fileName}`);
|
||||
}
|
||||
}
|
||||
|
||||
public printNavigationBar() {
|
||||
const items = this.languageService.getNavigationBarItems(this.activeFile.fileName);
|
||||
const length = items && items.length;
|
||||
|
||||
Harness.IO.log(`Navigation bar (${length} items)`);
|
||||
|
||||
for (let i = 0; i < length; i++) {
|
||||
const item = items[i];
|
||||
Harness.IO.log(`Navigation bar (${items.length} items)`);
|
||||
for (const item of items) {
|
||||
Harness.IO.log(`${repeatString(item.indent, " ")}name: ${item.text}, kind: ${item.kind}, childItems: ${item.childItems.map(child => child.text)}`);
|
||||
}
|
||||
}
|
||||
@@ -2407,8 +2357,7 @@ namespace FourSlash {
|
||||
}
|
||||
|
||||
private assertItemInCompletionList(items: ts.CompletionEntry[], name: string, text?: string, documentation?: string, kind?: string, spanIndex?: number) {
|
||||
for (let i = 0; i < items.length; i++) {
|
||||
const item = items[i];
|
||||
for (const item of items) {
|
||||
if (item.name === name) {
|
||||
if (documentation != undefined || text !== undefined) {
|
||||
const details = this.getCompletionEntryDetails(item.name);
|
||||
@@ -2457,20 +2406,17 @@ namespace FourSlash {
|
||||
name = name.indexOf("/") === -1 ? (this.basePath + "/" + name) : name;
|
||||
|
||||
const availableNames: string[] = [];
|
||||
let foundIt = false;
|
||||
for (let i = 0; i < this.testData.files.length; i++) {
|
||||
const fn = this.testData.files[i].fileName;
|
||||
result = ts.forEach(this.testData.files, file => {
|
||||
const fn = file.fileName;
|
||||
if (fn) {
|
||||
if (fn === name) {
|
||||
result = this.testData.files[i];
|
||||
foundIt = true;
|
||||
break;
|
||||
return file;
|
||||
}
|
||||
availableNames.push(fn);
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
if (!foundIt) {
|
||||
if (!result) {
|
||||
throw new Error(`No test file named "${name}" exists. Available file names are: ${availableNames.join(", ")}`);
|
||||
}
|
||||
}
|
||||
@@ -2571,8 +2517,8 @@ ${code}
|
||||
|
||||
function chompLeadingSpace(content: string) {
|
||||
const lines = content.split("\n");
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
if ((lines[i].length !== 0) && (lines[i].charAt(0) !== " ")) {
|
||||
for (const line of lines) {
|
||||
if ((line.length !== 0) && (line.charAt(0) !== " ")) {
|
||||
return content;
|
||||
}
|
||||
}
|
||||
@@ -2610,8 +2556,7 @@ ${code}
|
||||
currentFileName = fileName;
|
||||
}
|
||||
|
||||
for (let i = 0; i < lines.length; i++) {
|
||||
let line = lines[i];
|
||||
for (let line of lines) {
|
||||
const lineLength = line.length;
|
||||
|
||||
if (lineLength > 0 && line.charAt(lineLength - 1) === "\r") {
|
||||
@@ -2658,7 +2603,7 @@ ${code}
|
||||
resetLocalData();
|
||||
}
|
||||
|
||||
currentFileName = basePath + "/" + value;
|
||||
currentFileName = ts.isRootedDiskPath(value) ? value : basePath + "/" + value;
|
||||
currentFileOptions[key] = value;
|
||||
}
|
||||
else {
|
||||
@@ -3081,19 +3026,8 @@ namespace FourSlashInterface {
|
||||
}
|
||||
}
|
||||
|
||||
// Verifies the member list contains the specified symbol. The
|
||||
// member list is brought up if necessary
|
||||
public memberListContains(symbol: string, text?: string, documentation?: string, kind?: string) {
|
||||
if (this.negative) {
|
||||
this.state.verifyMemberListDoesNotContain(symbol);
|
||||
}
|
||||
else {
|
||||
this.state.verifyMemberListContains(symbol, text, documentation, kind);
|
||||
}
|
||||
}
|
||||
|
||||
public memberListCount(expectedCount: number) {
|
||||
this.state.verifyMemberListCount(expectedCount, this.negative);
|
||||
public completionListCount(expectedCount: number) {
|
||||
this.state.verifyCompletionListCount(expectedCount, this.negative);
|
||||
}
|
||||
|
||||
// Verifies the completion list contains the specified symbol. The
|
||||
@@ -3129,10 +3063,6 @@ namespace FourSlashInterface {
|
||||
this.state.verifyCompletionListAllowsNewIdentifier(this.negative);
|
||||
}
|
||||
|
||||
public memberListIsEmpty() {
|
||||
this.state.verifyMemberListIsEmpty(this.negative);
|
||||
}
|
||||
|
||||
public signatureHelpPresent() {
|
||||
this.state.verifySignatureHelpPresent(!this.negative);
|
||||
}
|
||||
@@ -3287,6 +3217,10 @@ namespace FourSlashInterface {
|
||||
this.state.verifySignatureHelpCount(expected);
|
||||
}
|
||||
|
||||
public signatureHelpCurrentArgumentListIsVariadic(expected: boolean) {
|
||||
this.state.verifyCurrentSignatureHelpIsVariadic(expected);
|
||||
}
|
||||
|
||||
public signatureHelpArgumentCountIs(expected: number) {
|
||||
this.state.verifySignatureHelpArgumentCount(expected);
|
||||
}
|
||||
@@ -3351,6 +3285,10 @@ namespace FourSlashInterface {
|
||||
this.state.verifyCodeFixAtPosition(expectedText, errorCode);
|
||||
}
|
||||
|
||||
public importFixAtPosition(expectedTextArray: string[], errorCode?: number): void {
|
||||
this.state.verifyImportFixAtPosition(expectedTextArray, errorCode);
|
||||
}
|
||||
|
||||
public navigationBar(json: any) {
|
||||
this.state.verifyNavigationBar(json);
|
||||
}
|
||||
@@ -3526,10 +3464,6 @@ namespace FourSlashInterface {
|
||||
this.state.printCurrentSignatureHelp();
|
||||
}
|
||||
|
||||
public printMemberListMembers() {
|
||||
this.state.printMemberListMembers();
|
||||
}
|
||||
|
||||
public printCompletionListMembers() {
|
||||
this.state.printCompletionListMembers();
|
||||
}
|
||||
@@ -3590,11 +3524,8 @@ namespace FourSlashInterface {
|
||||
this.state.formatOnType(this.state.getMarkerByName(posMarker).position, key);
|
||||
}
|
||||
|
||||
public setOption(name: string, value: number): void;
|
||||
public setOption(name: string, value: string): void;
|
||||
public setOption(name: string, value: boolean): void;
|
||||
public setOption(name: string, value: any): void {
|
||||
(<any>this.state.formatCodeSettings)[name] = value;
|
||||
public setOption(name: keyof ts.FormatCodeSettings, value: number | string | boolean): void {
|
||||
this.state.formatCodeSettings[name] = value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+125
-102
@@ -85,7 +85,7 @@ namespace Utils {
|
||||
eval(fileContents);
|
||||
break;
|
||||
case ExecutionEnvironment.Node:
|
||||
let vm = require("vm");
|
||||
const vm = require("vm");
|
||||
if (nodeContext) {
|
||||
vm.runInNewContext(fileContents, nodeContext, fileName);
|
||||
}
|
||||
@@ -175,9 +175,9 @@ namespace Utils {
|
||||
assert.isFalse(array.end > node.end, "array.end > node.end");
|
||||
assert.isFalse(array.pos < currentPos, "array.pos < currentPos");
|
||||
|
||||
for (let i = 0, n = array.length; i < n; i++) {
|
||||
assert.isFalse(array[i].pos < currentPos, "array[i].pos < currentPos");
|
||||
currentPos = array[i].end;
|
||||
for (const item of array) {
|
||||
assert.isFalse(item.pos < currentPos, "array[i].pos < currentPos");
|
||||
currentPos = item.end;
|
||||
}
|
||||
|
||||
currentPos = array.end;
|
||||
@@ -220,9 +220,7 @@ namespace Utils {
|
||||
}
|
||||
|
||||
export function sourceFileToJSON(file: ts.Node): string {
|
||||
return JSON.stringify(file, (k, v) => {
|
||||
return isNodeOrArray(v) ? serializeNode(v) : v;
|
||||
}, " ");
|
||||
return JSON.stringify(file, (_, v) => isNodeOrArray(v) ? serializeNode(v) : v, " ");
|
||||
|
||||
function getKindName(k: number | string): string {
|
||||
if (typeof k === "string") {
|
||||
@@ -346,7 +344,7 @@ namespace Utils {
|
||||
|
||||
assert.equal(array1.length, array2.length, "array1.length !== array2.length");
|
||||
|
||||
for (let i = 0, n = array1.length; i < n; i++) {
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
const d1 = array1[i];
|
||||
const d2 = array2[i];
|
||||
|
||||
@@ -402,7 +400,7 @@ namespace Utils {
|
||||
assert.equal(array1.end, array2.end, "array1.end !== array2.end");
|
||||
assert.equal(array1.length, array2.length, "array1.length !== array2.length");
|
||||
|
||||
for (let i = 0, n = array1.length; i < n; i++) {
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
assertStructuralEquals(array1[i], array2[i]);
|
||||
}
|
||||
}
|
||||
@@ -472,19 +470,6 @@ namespace Utils {
|
||||
}
|
||||
}
|
||||
|
||||
namespace Harness.Path {
|
||||
export function getFileName(fullPath: string) {
|
||||
return fullPath.replace(/^.*[\\\/]/, "");
|
||||
}
|
||||
|
||||
export function filePath(fullPath: string) {
|
||||
fullPath = ts.normalizeSlashes(fullPath);
|
||||
const components = fullPath.split("/");
|
||||
const path: string[] = components.slice(0, components.length - 1);
|
||||
return path.join("/") + "/";
|
||||
}
|
||||
}
|
||||
|
||||
namespace Harness {
|
||||
export interface IO {
|
||||
newLine(): string;
|
||||
@@ -692,7 +677,7 @@ namespace Harness {
|
||||
export const getCurrentDirectory = () => "";
|
||||
export const args = () => <string[]>[];
|
||||
export const getExecutingFilePath = () => "";
|
||||
export const exit = (exitCode: number) => { };
|
||||
export const exit = ts.noop;
|
||||
export const getDirectories = () => <string[]>[];
|
||||
|
||||
export let log = (s: string) => console.log(s);
|
||||
@@ -742,7 +727,7 @@ namespace Harness {
|
||||
}
|
||||
}
|
||||
|
||||
export function createDirectory(path: string) {
|
||||
export function createDirectory() {
|
||||
// Do nothing (?)
|
||||
}
|
||||
|
||||
@@ -750,7 +735,7 @@ namespace Harness {
|
||||
Http.writeToServerSync(serverRoot + path, "DELETE");
|
||||
}
|
||||
|
||||
export function directoryExists(path: string): boolean {
|
||||
export function directoryExists(): boolean {
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -792,7 +777,7 @@ namespace Harness {
|
||||
return response.status === 200;
|
||||
}
|
||||
|
||||
export function _listFilesImpl(path: string, spec?: RegExp, options?: any) {
|
||||
export let listFiles = Utils.memoize((path: string, spec?: RegExp): string[] => {
|
||||
const response = Http.getFileFromServerSync(serverRoot + path);
|
||||
if (response.status === 200) {
|
||||
const results = response.responseText.split(",");
|
||||
@@ -806,8 +791,7 @@ namespace Harness {
|
||||
else {
|
||||
return [""];
|
||||
}
|
||||
};
|
||||
export let listFiles = Utils.memoize(_listFilesImpl);
|
||||
});
|
||||
|
||||
export function readFile(file: string) {
|
||||
const response = Http.getFileFromServerSync(serverRoot + file);
|
||||
@@ -941,7 +925,17 @@ namespace Harness {
|
||||
}
|
||||
|
||||
export function getDefaultLibFileName(options: ts.CompilerOptions): string {
|
||||
return options.target === ts.ScriptTarget.ES6 ? es2015DefaultLibFileName : defaultLibFileName;
|
||||
switch (options.target) {
|
||||
case ts.ScriptTarget.ES2017:
|
||||
return "lib.es2017.d.ts";
|
||||
case ts.ScriptTarget.ES2016:
|
||||
return "lib.es2016.d.ts";
|
||||
case ts.ScriptTarget.ES2015:
|
||||
return es2015DefaultLibFileName;
|
||||
|
||||
default:
|
||||
return defaultLibFileName;
|
||||
}
|
||||
}
|
||||
|
||||
// Cache these between executions so we don't have to re-parse them for every test
|
||||
@@ -964,28 +958,38 @@ namespace Harness {
|
||||
// Local get canonical file name function, that depends on passed in parameter for useCaseSensitiveFileNames
|
||||
const getCanonicalFileName = ts.createGetCanonicalFileName(useCaseSensitiveFileNames);
|
||||
|
||||
const realPathMap: ts.FileMap<string> = ts.createFileMap<string>();
|
||||
const fileMap: ts.FileMap<() => ts.SourceFile> = ts.createFileMap<() => ts.SourceFile>();
|
||||
/** Maps a symlink name to a realpath. Used only for exposing `realpath`. */
|
||||
const realPathMap = ts.createFileMap<string>();
|
||||
/**
|
||||
* Maps a file name to a source file.
|
||||
* This will have a different SourceFile for every symlink pointing to that file;
|
||||
* if the program resolves realpaths then symlink entries will be ignored.
|
||||
*/
|
||||
const fileMap = ts.createFileMap<ts.SourceFile>();
|
||||
for (const file of inputFiles) {
|
||||
if (file.content !== undefined) {
|
||||
const fileName = ts.normalizePath(file.unitName);
|
||||
const path = ts.toPath(file.unitName, currentDirectory, getCanonicalFileName);
|
||||
if (file.fileOptions && file.fileOptions["symlink"]) {
|
||||
const link = file.fileOptions["symlink"];
|
||||
const linkPath = ts.toPath(link, currentDirectory, getCanonicalFileName);
|
||||
realPathMap.set(linkPath, fileName);
|
||||
fileMap.set(path, (): ts.SourceFile => { throw new Error("Symlinks should always be resolved to a realpath first"); });
|
||||
const links = file.fileOptions["symlink"].split(",");
|
||||
for (const link of links) {
|
||||
const linkPath = ts.toPath(link, currentDirectory, getCanonicalFileName);
|
||||
realPathMap.set(linkPath, fileName);
|
||||
// Create a different SourceFile for every symlink.
|
||||
const sourceFile = createSourceFileAndAssertInvariants(linkPath, file.content, scriptTarget);
|
||||
fileMap.set(linkPath, sourceFile);
|
||||
}
|
||||
}
|
||||
const sourceFile = createSourceFileAndAssertInvariants(fileName, file.content, scriptTarget);
|
||||
fileMap.set(path, () => sourceFile);
|
||||
fileMap.set(path, sourceFile);
|
||||
}
|
||||
}
|
||||
|
||||
function getSourceFile(fileName: string, languageVersion: ts.ScriptTarget) {
|
||||
function getSourceFile(fileName: string) {
|
||||
fileName = ts.normalizePath(fileName);
|
||||
const path = ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
if (fileMap.contains(path)) {
|
||||
return fileMap.get(path)();
|
||||
const fromFileMap = fileMap.get(toPath(fileName));
|
||||
if (fromFileMap) {
|
||||
return fromFileMap;
|
||||
}
|
||||
else if (fileName === fourslashFileName) {
|
||||
const tsFn = "tests/cases/fourslash/" + fourslashFileName;
|
||||
@@ -1004,6 +1008,9 @@ namespace Harness {
|
||||
newLineKind === ts.NewLineKind.LineFeed ? lineFeed :
|
||||
Harness.IO.newLine();
|
||||
|
||||
function toPath(fileName: string): ts.Path {
|
||||
return ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
}
|
||||
|
||||
return {
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
@@ -1013,35 +1020,31 @@ namespace Harness {
|
||||
getCanonicalFileName,
|
||||
useCaseSensitiveFileNames: () => useCaseSensitiveFileNames,
|
||||
getNewLine: () => newLine,
|
||||
fileExists: fileName => {
|
||||
const path = ts.toPath(fileName, currentDirectory, getCanonicalFileName);
|
||||
return fileMap.contains(path) || (realPathMap && realPathMap.contains(path));
|
||||
},
|
||||
fileExists: fileName => fileMap.contains(toPath(fileName)),
|
||||
readFile: (fileName: string): string => {
|
||||
return fileMap.get(ts.toPath(fileName, currentDirectory, getCanonicalFileName))().getText();
|
||||
const file = fileMap.get(toPath(fileName));
|
||||
if (ts.endsWith(fileName, "json")) {
|
||||
// strip comments
|
||||
return file.getText();
|
||||
}
|
||||
return file.text;
|
||||
},
|
||||
realpath: (fileName: string): ts.Path => {
|
||||
const path = toPath(fileName);
|
||||
return (realPathMap.get(path) as ts.Path) || path;
|
||||
},
|
||||
realpath: realPathMap && ((f: string) => {
|
||||
const path = ts.toPath(f, currentDirectory, getCanonicalFileName);
|
||||
return realPathMap.contains(path) ? realPathMap.get(path) : path;
|
||||
}),
|
||||
directoryExists: dir => {
|
||||
let path = ts.toPath(dir, currentDirectory, getCanonicalFileName);
|
||||
// Strip trailing /, which may exist if the path is a drive root
|
||||
if (path[path.length - 1] === "/") {
|
||||
path = <ts.Path>path.substr(0, path.length - 1);
|
||||
}
|
||||
let exists = false;
|
||||
fileMap.forEachValue(key => {
|
||||
if (key.indexOf(path) === 0 && key[path.length] === "/") {
|
||||
exists = true;
|
||||
}
|
||||
});
|
||||
return exists;
|
||||
return mapHasFileInDirectory(path, fileMap);
|
||||
},
|
||||
getDirectories: d => {
|
||||
const path = ts.toPath(d, currentDirectory, getCanonicalFileName);
|
||||
const result: string[] = [];
|
||||
fileMap.forEachValue((key, value) => {
|
||||
fileMap.forEachValue(key => {
|
||||
if (key.indexOf(path) === 0 && key.lastIndexOf("/") > path.length) {
|
||||
let dirName = key.substr(path.length, key.indexOf("/", path.length + 1) - path.length);
|
||||
if (dirName[0] === "/") {
|
||||
@@ -1057,6 +1060,19 @@ namespace Harness {
|
||||
};
|
||||
}
|
||||
|
||||
function mapHasFileInDirectory(directoryPath: ts.Path, map: ts.FileMap<any>): boolean {
|
||||
if (!map) {
|
||||
return false;
|
||||
}
|
||||
let exists = false;
|
||||
map.forEachValue(fileName => {
|
||||
if (!exists && ts.startsWith(fileName, directoryPath) && fileName[directoryPath.length] === "/") {
|
||||
exists = true;
|
||||
}
|
||||
});
|
||||
return exists;
|
||||
}
|
||||
|
||||
interface HarnessOptions {
|
||||
useCaseSensitiveFileNames?: boolean;
|
||||
includeBuiltFile?: string;
|
||||
@@ -1076,7 +1092,9 @@ namespace Harness {
|
||||
{ name: "suppressOutputPathCheck", type: "boolean" },
|
||||
{ name: "noImplicitReferences", type: "boolean" },
|
||||
{ name: "currentDirectory", type: "string" },
|
||||
{ name: "symlink", type: "string" }
|
||||
{ name: "symlink", type: "string" },
|
||||
// Emitted js baseline will print full paths for every output file
|
||||
{ name: "fullEmitPaths", type: "boolean" }
|
||||
];
|
||||
|
||||
let optionsIndex: ts.Map<ts.CommandLineOption>;
|
||||
@@ -1101,22 +1119,7 @@ namespace Harness {
|
||||
const option = getCommandLineOption(name);
|
||||
if (option) {
|
||||
const errors: ts.Diagnostic[] = [];
|
||||
switch (option.type) {
|
||||
case "boolean":
|
||||
options[option.name] = value.toLowerCase() === "true";
|
||||
break;
|
||||
case "string":
|
||||
options[option.name] = value;
|
||||
break;
|
||||
// If not a primitive, the possible types are specified in what is effectively a map of options.
|
||||
case "list":
|
||||
options[option.name] = ts.parseListTypeOption(<ts.CommandLineOptionOfListType>option, value, errors);
|
||||
break;
|
||||
default:
|
||||
options[option.name] = ts.parseCustomTypeOption(<ts.CommandLineOptionOfCustomType>option, value, errors);
|
||||
break;
|
||||
}
|
||||
|
||||
options[option.name] = optionValue(option, value, errors);
|
||||
if (errors.length > 0) {
|
||||
throw new Error(`Unknown value '${value}' for compiler option '${name}'.`);
|
||||
}
|
||||
@@ -1128,6 +1131,27 @@ namespace Harness {
|
||||
}
|
||||
}
|
||||
|
||||
function optionValue(option: ts.CommandLineOption, value: string, errors: ts.Diagnostic[]): any {
|
||||
switch (option.type) {
|
||||
case "boolean":
|
||||
return value.toLowerCase() === "true";
|
||||
case "string":
|
||||
return value;
|
||||
case "number": {
|
||||
const number = parseInt(value, 10);
|
||||
if (isNaN(number)) {
|
||||
throw new Error(`Value must be a number, got: ${JSON.stringify(value)}`);
|
||||
}
|
||||
return number;
|
||||
}
|
||||
// If not a primitive, the possible types are specified in what is effectively a map of options.
|
||||
case "list":
|
||||
return ts.parseListTypeOption(<ts.CommandLineOptionOfListType>option, value, errors);
|
||||
default:
|
||||
return ts.parseCustomTypeOption(<ts.CommandLineOptionOfCustomType>option, value, errors);
|
||||
}
|
||||
}
|
||||
|
||||
export interface TestFile {
|
||||
unitName: string;
|
||||
content: string;
|
||||
@@ -1231,7 +1255,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);
|
||||
const output = compileFiles(declInputFiles, declOtherFiles, harnessSettings, options, currentDirectory || harnessSettings["currentDirectory"]);
|
||||
return { declInputFiles, declOtherFiles, declResult: output.result };
|
||||
}
|
||||
|
||||
@@ -1463,7 +1487,7 @@ namespace Harness {
|
||||
}
|
||||
|
||||
if (typesError && symbolsError) {
|
||||
throw new Error(typesError.message + ts.sys.newLine + symbolsError.message);
|
||||
throw new Error(typesError.message + Harness.IO.newLine() + symbolsError.message);
|
||||
}
|
||||
|
||||
if (typesError) {
|
||||
@@ -1545,7 +1569,7 @@ namespace Harness {
|
||||
return file.writeByteOrderMark ? "\u00EF\u00BB\u00BF" : "";
|
||||
}
|
||||
|
||||
export function doSourcemapBaseline(baselinePath: string, options: ts.CompilerOptions, result: CompilerResult) {
|
||||
export function doSourcemapBaseline(baselinePath: string, options: ts.CompilerOptions, result: CompilerResult, harnessSettings: Harness.TestCaseParser.CompilerSettings) {
|
||||
if (options.inlineSourceMap) {
|
||||
if (result.sourceMaps.length > 0) {
|
||||
throw new Error("No sourcemap files should be generated if inlineSourceMaps was set.");
|
||||
@@ -1567,10 +1591,8 @@ namespace Harness {
|
||||
}
|
||||
|
||||
let sourceMapCode = "";
|
||||
for (let i = 0; i < result.sourceMaps.length; i++) {
|
||||
sourceMapCode += "//// [" + Harness.Path.getFileName(result.sourceMaps[i].fileName) + "]\r\n";
|
||||
sourceMapCode += getByteOrderMarkText(result.sourceMaps[i]);
|
||||
sourceMapCode += result.sourceMaps[i].code;
|
||||
for (const sourceMap of result.sourceMaps) {
|
||||
sourceMapCode += fileOutput(sourceMap, harnessSettings);
|
||||
}
|
||||
|
||||
return sourceMapCode;
|
||||
@@ -1591,23 +1613,19 @@ namespace Harness {
|
||||
tsCode += "//// [" + header + "] ////\r\n\r\n";
|
||||
}
|
||||
for (let i = 0; i < tsSources.length; i++) {
|
||||
tsCode += "//// [" + Harness.Path.getFileName(tsSources[i].unitName) + "]\r\n";
|
||||
tsCode += "//// [" + ts.getBaseFileName(tsSources[i].unitName) + "]\r\n";
|
||||
tsCode += tsSources[i].content + (i < (tsSources.length - 1) ? "\r\n" : "");
|
||||
}
|
||||
|
||||
let jsCode = "";
|
||||
for (let i = 0; i < result.files.length; i++) {
|
||||
jsCode += "//// [" + Harness.Path.getFileName(result.files[i].fileName) + "]\r\n";
|
||||
jsCode += getByteOrderMarkText(result.files[i]);
|
||||
jsCode += result.files[i].code;
|
||||
for (const file of result.files) {
|
||||
jsCode += fileOutput(file, harnessSettings);
|
||||
}
|
||||
|
||||
if (result.declFilesCode.length > 0) {
|
||||
jsCode += "\r\n\r\n";
|
||||
for (let i = 0; i < result.declFilesCode.length; i++) {
|
||||
jsCode += "//// [" + Harness.Path.getFileName(result.declFilesCode[i].fileName) + "]\r\n";
|
||||
jsCode += getByteOrderMarkText(result.declFilesCode[i]);
|
||||
jsCode += result.declFilesCode[i].code;
|
||||
for (const declFile of result.declFilesCode) {
|
||||
jsCode += fileOutput(declFile, harnessSettings);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1632,9 +1650,14 @@ namespace Harness {
|
||||
});
|
||||
}
|
||||
|
||||
function fileOutput(file: GeneratedFile, harnessSettings: Harness.TestCaseParser.CompilerSettings): string {
|
||||
const fileName = harnessSettings["fullEmitPaths"] ? file.fileName : ts.getBaseFileName(file.fileName);
|
||||
return "//// [" + fileName + "]\r\n" + getByteOrderMarkText(file) + file.code;
|
||||
}
|
||||
|
||||
export function collateOutputs(outputFiles: Harness.Compiler.GeneratedFile[]): string {
|
||||
// Collect, test, and sort the fileNames
|
||||
outputFiles.sort((a, b) => cleanName(a.fileName).localeCompare(cleanName(b.fileName)));
|
||||
outputFiles.sort((a, b) => ts.compareStrings(cleanName(a.fileName), cleanName(b.fileName)));
|
||||
|
||||
// Emit them
|
||||
let result = "";
|
||||
@@ -1659,9 +1682,9 @@ namespace Harness {
|
||||
}
|
||||
|
||||
// This does not need to exist strictly speaking, but many tests will need to be updated if it's removed
|
||||
export function compileString(code: string, unitName: string, callback: (result: CompilerResult) => void) {
|
||||
export function compileString(_code: string, _unitName: string, _callback: (result: CompilerResult) => void) {
|
||||
// NEWTODO: Re-implement 'compileString'
|
||||
throw new Error("compileString NYI");
|
||||
return ts.notImplemented();
|
||||
}
|
||||
|
||||
export interface GeneratedFile {
|
||||
@@ -1748,7 +1771,7 @@ namespace Harness {
|
||||
}
|
||||
|
||||
// Regex for parsing options in the format "@Alpha: Value of any sort"
|
||||
const optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*(\S*)/gm; // multiple matches on multiple lines
|
||||
const optionRegex = /^[\/]{2}\s*@(\w+)\s*:\s*([^\r\n]*)/gm; // multiple matches on multiple lines
|
||||
|
||||
function extractCompilerSettings(content: string): CompilerSettings {
|
||||
const opts: CompilerSettings = {};
|
||||
@@ -1757,7 +1780,7 @@ namespace Harness {
|
||||
/* tslint:disable:no-null-keyword */
|
||||
while ((match = optionRegex.exec(content)) !== null) {
|
||||
/* tslint:enable:no-null-keyword */
|
||||
opts[match[1]] = match[2];
|
||||
opts[match[1]] = match[2].trim();
|
||||
}
|
||||
|
||||
return opts;
|
||||
@@ -1785,7 +1808,7 @@ namespace Harness {
|
||||
// Comment line, check for global/file @options and record them
|
||||
optionRegex.lastIndex = 0;
|
||||
const metaDataName = testMetaData[1].toLowerCase();
|
||||
currentFileOptions[testMetaData[1]] = testMetaData[2];
|
||||
currentFileOptions[testMetaData[1]] = testMetaData[2].trim();
|
||||
if (metaDataName !== "filename") {
|
||||
continue;
|
||||
}
|
||||
@@ -1805,12 +1828,12 @@ namespace Harness {
|
||||
// Reset local data
|
||||
currentFileContent = undefined;
|
||||
currentFileOptions = {};
|
||||
currentFileName = testMetaData[2];
|
||||
currentFileName = testMetaData[2].trim();
|
||||
refs = [];
|
||||
}
|
||||
else {
|
||||
// First metadata marker in the file
|
||||
currentFileName = testMetaData[2];
|
||||
currentFileName = testMetaData[2].trim();
|
||||
}
|
||||
}
|
||||
else {
|
||||
@@ -1828,7 +1851,7 @@ namespace Harness {
|
||||
}
|
||||
|
||||
// normalize the fileName for the single file case
|
||||
currentFileName = testUnitData.length > 0 || currentFileName ? currentFileName : Path.getFileName(fileName);
|
||||
currentFileName = testUnitData.length > 0 || currentFileName ? currentFileName : ts.getBaseFileName(fileName);
|
||||
|
||||
// EOF, push whatever remains
|
||||
const newTestFile2 = {
|
||||
@@ -1843,8 +1866,8 @@ namespace Harness {
|
||||
// unit tests always list files explicitly
|
||||
const parseConfigHost: ts.ParseConfigHost = {
|
||||
useCaseSensitiveFileNames: false,
|
||||
readDirectory: (name) => [],
|
||||
fileExists: (name) => true,
|
||||
readDirectory: () => [],
|
||||
fileExists: () => true,
|
||||
readFile: (name) => ts.forEach(testUnitData, data => data.name.toLowerCase() === name.toLowerCase() ? data.content : undefined)
|
||||
};
|
||||
|
||||
@@ -1992,7 +2015,7 @@ namespace Harness {
|
||||
|
||||
export function isDefaultLibraryFile(filePath: string): boolean {
|
||||
// We need to make sure that the filePath is prefixed with "lib." not just containing "lib." and end with ".d.ts"
|
||||
const fileName = Path.getFileName(filePath);
|
||||
const fileName = ts.getBaseFileName(ts.normalizeSlashes(filePath));
|
||||
return ts.startsWith(fileName, "lib.") && ts.endsWith(fileName, ".d.ts");
|
||||
}
|
||||
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace Harness.LanguageService {
|
||||
throw new Error("No script with name '" + fileName + "'");
|
||||
}
|
||||
|
||||
public openFile(fileName: string, content?: string, scriptKindName?: string): void {
|
||||
public openFile(_fileName: string, _content?: string, _scriptKindName?: string): void {
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -198,7 +198,7 @@ namespace Harness.LanguageService {
|
||||
const script = this.getScriptInfo(fileName);
|
||||
return script ? new ScriptSnapshot(script) : undefined;
|
||||
}
|
||||
getScriptKind(fileName: string): ts.ScriptKind { return ts.ScriptKind.Unknown; }
|
||||
getScriptKind(): ts.ScriptKind { return ts.ScriptKind.Unknown; }
|
||||
getScriptVersion(fileName: string): string {
|
||||
const script = this.getScriptInfo(fileName);
|
||||
return script ? script.version.toString() : undefined;
|
||||
@@ -214,7 +214,7 @@ namespace Harness.LanguageService {
|
||||
this.getCurrentDirectory(),
|
||||
(p) => this.virtualFileSystem.getAccessibleFileSystemEntries(p));
|
||||
}
|
||||
readFile(path: string, encoding?: string): string {
|
||||
readFile(path: string): string {
|
||||
const snapshot = this.getScriptSnapshot(path);
|
||||
return snapshot.getText(0, snapshot.getLength());
|
||||
}
|
||||
@@ -223,9 +223,9 @@ namespace Harness.LanguageService {
|
||||
}
|
||||
|
||||
|
||||
log(s: string): void { }
|
||||
trace(s: string): void { }
|
||||
error(s: string): void { }
|
||||
log(_: string): void { }
|
||||
trace(_: string): void { }
|
||||
error(_: string): void { }
|
||||
}
|
||||
|
||||
export class NativeLanguageServiceAdapter implements LanguageServiceAdapter {
|
||||
@@ -308,19 +308,15 @@ namespace Harness.LanguageService {
|
||||
const nativeScriptSnapshot = this.nativeHost.getScriptSnapshot(fileName);
|
||||
return nativeScriptSnapshot && new ScriptSnapshotProxy(nativeScriptSnapshot);
|
||||
}
|
||||
getScriptKind(fileName: string): ts.ScriptKind { return this.nativeHost.getScriptKind(fileName); }
|
||||
getScriptKind(): ts.ScriptKind { return this.nativeHost.getScriptKind(); }
|
||||
getScriptVersion(fileName: string): string { return this.nativeHost.getScriptVersion(fileName); }
|
||||
getLocalizedDiagnosticMessages(): string { return JSON.stringify({}); }
|
||||
|
||||
readDirectory(rootDir: string, extension: string): string {
|
||||
throw new Error("NYI");
|
||||
}
|
||||
readDirectoryNames(path: string): string {
|
||||
throw new Error("Not implemented.");
|
||||
}
|
||||
readFileNames(path: string): string {
|
||||
throw new Error("Not implemented.");
|
||||
readDirectory(_rootDir: string, _extension: string): string {
|
||||
return ts.notImplemented();
|
||||
}
|
||||
readDirectoryNames = ts.notImplemented;
|
||||
readFileNames = ts.notImplemented;
|
||||
fileExists(fileName: string) { return this.getScriptInfo(fileName) !== undefined; }
|
||||
readFile(fileName: string) {
|
||||
const snapshot = this.nativeHost.getScriptSnapshot(fileName);
|
||||
@@ -329,7 +325,7 @@ namespace Harness.LanguageService {
|
||||
log(s: string): void { this.nativeHost.log(s); }
|
||||
trace(s: string): void { this.nativeHost.trace(s); }
|
||||
error(s: string): void { this.nativeHost.error(s); }
|
||||
directoryExists(directoryName: string): boolean {
|
||||
directoryExists(): boolean {
|
||||
// for tests pessimistically assume that directory always exists
|
||||
return true;
|
||||
}
|
||||
@@ -338,8 +334,8 @@ namespace Harness.LanguageService {
|
||||
class ClassifierShimProxy implements ts.Classifier {
|
||||
constructor(private shim: ts.ClassifierShim) {
|
||||
}
|
||||
getEncodedLexicalClassifications(text: string, lexState: ts.EndOfLineState, classifyKeywordsInGenerics?: boolean): ts.Classifications {
|
||||
throw new Error("NYI");
|
||||
getEncodedLexicalClassifications(_text: string, _lexState: ts.EndOfLineState, _classifyKeywordsInGenerics?: boolean): ts.Classifications {
|
||||
return ts.notImplemented();
|
||||
}
|
||||
getClassificationsForLine(text: string, lexState: ts.EndOfLineState, classifyKeywordsInGenerics?: boolean): ts.ClassificationResult {
|
||||
const result = this.shim.getClassificationsForLine(text, lexState, classifyKeywordsInGenerics).split("\n");
|
||||
@@ -411,7 +407,7 @@ namespace Harness.LanguageService {
|
||||
getCompletionEntryDetails(fileName: string, position: number, entryName: string): ts.CompletionEntryDetails {
|
||||
return unwrapJSONCallResult(this.shim.getCompletionEntryDetails(fileName, position, entryName));
|
||||
}
|
||||
getCompletionEntrySymbol(fileName: string, position: number, entryName: string): ts.Symbol {
|
||||
getCompletionEntrySymbol(): ts.Symbol {
|
||||
throw new Error("getCompletionEntrySymbol not implemented across the shim layer.");
|
||||
}
|
||||
getQuickInfoAtPosition(fileName: string, position: number): ts.QuickInfo {
|
||||
@@ -490,7 +486,7 @@ namespace Harness.LanguageService {
|
||||
isValidBraceCompletionAtPosition(fileName: string, position: number, openingBrace: number): boolean {
|
||||
return unwrapJSONCallResult(this.shim.isValidBraceCompletionAtPosition(fileName, position, openingBrace));
|
||||
}
|
||||
getCodeFixesAtPosition(fileName: string, start: number, end: number, errorCodes: number[]): ts.CodeAction[] {
|
||||
getCodeFixesAtPosition(): ts.CodeAction[] {
|
||||
throw new Error("Not supported on the shim.");
|
||||
}
|
||||
getEmitOutput(fileName: string): ts.EmitOutput {
|
||||
@@ -499,10 +495,10 @@ namespace Harness.LanguageService {
|
||||
getProgram(): ts.Program {
|
||||
throw new Error("Program can not be marshaled across the shim layer.");
|
||||
}
|
||||
getNonBoundSourceFile(fileName: string): ts.SourceFile {
|
||||
getNonBoundSourceFile(): ts.SourceFile {
|
||||
throw new Error("SourceFile can not be marshaled across the shim layer.");
|
||||
}
|
||||
getSourceFile(fileName: string): ts.SourceFile {
|
||||
getSourceFile(): ts.SourceFile {
|
||||
throw new Error("SourceFile can not be marshaled across the shim layer.");
|
||||
}
|
||||
dispose(): void { this.shim.dispose({}); }
|
||||
@@ -572,11 +568,11 @@ namespace Harness.LanguageService {
|
||||
super(cancellationToken, settings);
|
||||
}
|
||||
|
||||
onMessage(message: string): void {
|
||||
onMessage(): void {
|
||||
|
||||
}
|
||||
|
||||
writeMessage(message: string): void {
|
||||
writeMessage(): void {
|
||||
|
||||
}
|
||||
|
||||
@@ -604,11 +600,11 @@ namespace Harness.LanguageService {
|
||||
this.newLine = this.host.getNewLine();
|
||||
}
|
||||
|
||||
onMessage(message: string): void {
|
||||
onMessage(): void {
|
||||
|
||||
}
|
||||
|
||||
writeMessage(message: string): void {
|
||||
writeMessage(_message: string): void {
|
||||
}
|
||||
|
||||
write(message: string): void {
|
||||
@@ -624,7 +620,7 @@ namespace Harness.LanguageService {
|
||||
return snapshot && snapshot.getText(0, snapshot.getLength());
|
||||
}
|
||||
|
||||
writeFile(name: string, text: string, writeByteOrderMark: boolean): void {
|
||||
writeFile(): void {
|
||||
}
|
||||
|
||||
resolvePath(path: string): string {
|
||||
@@ -635,7 +631,7 @@ namespace Harness.LanguageService {
|
||||
return !!this.host.getScriptSnapshot(path);
|
||||
}
|
||||
|
||||
directoryExists(path: string): boolean {
|
||||
directoryExists(): boolean {
|
||||
// for tests assume that directory exists
|
||||
return true;
|
||||
}
|
||||
@@ -644,18 +640,18 @@ namespace Harness.LanguageService {
|
||||
return "";
|
||||
}
|
||||
|
||||
exit(exitCode: number): void {
|
||||
exit(): void {
|
||||
}
|
||||
|
||||
createDirectory(directoryName: string): void {
|
||||
throw new Error("Not Implemented Yet.");
|
||||
createDirectory(_directoryName: string): void {
|
||||
return ts.notImplemented();
|
||||
}
|
||||
|
||||
getCurrentDirectory(): string {
|
||||
return this.host.getCurrentDirectory();
|
||||
}
|
||||
|
||||
getDirectories(path: string): string[] {
|
||||
getDirectories(): string[] {
|
||||
return [];
|
||||
}
|
||||
|
||||
@@ -663,16 +659,16 @@ namespace Harness.LanguageService {
|
||||
return ts.sys.getEnvironmentVariable(name);
|
||||
}
|
||||
|
||||
readDirectory(path: string, extension?: string[], exclude?: string[], include?: string[]): string[] {
|
||||
throw new Error("Not implemented Yet.");
|
||||
readDirectory(_path: string, _extension?: string[], _exclude?: string[], _include?: string[]): string[] {
|
||||
return ts.notImplemented();
|
||||
}
|
||||
|
||||
watchFile(fileName: string, callback: (fileName: string) => void): ts.FileWatcher {
|
||||
return { close() { } };
|
||||
watchFile(): ts.FileWatcher {
|
||||
return { close: ts.noop };
|
||||
}
|
||||
|
||||
watchDirectory(path: string, callback: (path: string) => void, recursive?: boolean): ts.FileWatcher {
|
||||
return { close() { } };
|
||||
watchDirectory(): ts.FileWatcher {
|
||||
return { close: ts.noop };
|
||||
}
|
||||
|
||||
close(): void {
|
||||
@@ -717,13 +713,17 @@ namespace Harness.LanguageService {
|
||||
clearTimeout(timeoutId);
|
||||
}
|
||||
|
||||
setImmediate(callback: (...args: any[]) => void, ms: number, ...args: any[]): any {
|
||||
setImmediate(callback: (...args: any[]) => void, _ms: number, ...args: any[]): any {
|
||||
return setImmediate(callback, args);
|
||||
}
|
||||
|
||||
clearImmediate(timeoutId: any): void {
|
||||
clearImmediate(timeoutId);
|
||||
}
|
||||
|
||||
createHash(s: string) {
|
||||
return s;
|
||||
}
|
||||
}
|
||||
|
||||
export class ServerLanguageServiceAdapter implements LanguageServiceAdapter {
|
||||
@@ -760,6 +760,6 @@ namespace Harness.LanguageService {
|
||||
getHost() { return this.host; }
|
||||
getLanguageService(): ts.LanguageService { return this.client; }
|
||||
getClassifier(): ts.Classifier { throw new Error("getClassifier is not available using the server interface."); }
|
||||
getPreProcessedFileInfo(fileName: string, fileContents: string): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); }
|
||||
getPreProcessedFileInfo(): ts.PreProcessedFileInfo { throw new Error("getPreProcessedFileInfo is not available using the server interface."); }
|
||||
}
|
||||
}
|
||||
|
||||
+15
-11
@@ -173,7 +173,7 @@ namespace Playback {
|
||||
path => callAndRecord(underlying.fileExists(path), recordLog.fileExists, { path }),
|
||||
memoize(path => {
|
||||
// If we read from the file, it must exist
|
||||
if (findFileByPath(wrapper, replayLog.filesRead, path, /*throwFileNotFoundError*/ false)) {
|
||||
if (findFileByPath(replayLog.filesRead, path, /*throwFileNotFoundError*/ false)) {
|
||||
return true;
|
||||
}
|
||||
else {
|
||||
@@ -217,7 +217,7 @@ namespace Playback {
|
||||
recordLog.filesRead.push(logEntry);
|
||||
return result;
|
||||
},
|
||||
memoize(path => findFileByPath(wrapper, replayLog.filesRead, path, /*throwFileNotFoundError*/ true).contents));
|
||||
memoize(path => findFileByPath(replayLog.filesRead, path, /*throwFileNotFoundError*/ true).contents));
|
||||
|
||||
wrapper.readDirectory = recordReplay(wrapper.readDirectory, underlying)(
|
||||
(path, extensions, exclude, include) => {
|
||||
@@ -226,7 +226,7 @@ namespace Playback {
|
||||
recordLog.directoriesRead.push(logEntry);
|
||||
return result;
|
||||
},
|
||||
(path, extensions, exclude) => {
|
||||
path => {
|
||||
// Because extensions is an array of all allowed extension, we will want to merge each of the replayLog.directoriesRead into one
|
||||
// if each of the directoriesRead has matched path with the given path (directory with same path but different extension will considered
|
||||
// different entry).
|
||||
@@ -244,7 +244,7 @@ namespace Playback {
|
||||
|
||||
wrapper.writeFile = recordReplay(wrapper.writeFile, underlying)(
|
||||
(path: string, contents: string) => callAndRecord(underlying.writeFile(path, contents), recordLog.filesWritten, { path, contents, bom: false }),
|
||||
(path: string, contents: string) => noOpReplay("writeFile"));
|
||||
() => noOpReplay("writeFile"));
|
||||
|
||||
wrapper.exit = (exitCode) => {
|
||||
if (recordLog !== undefined) {
|
||||
@@ -295,7 +295,7 @@ namespace Playback {
|
||||
return results[0].result;
|
||||
}
|
||||
|
||||
function findFileByPath(wrapper: { resolvePath(s: string): string }, logArray: IOLogFile[],
|
||||
function findFileByPath(logArray: IOLogFile[],
|
||||
expectedPath: string, throwFileNotFoundError: boolean): FileInformation {
|
||||
const normalizedName = ts.normalizePath(expectedPath).toLowerCase();
|
||||
// Try to find the result through normal fileName
|
||||
@@ -314,7 +314,7 @@ namespace Playback {
|
||||
}
|
||||
}
|
||||
|
||||
function noOpReplay(name: string) {
|
||||
function noOpReplay(_name: string) {
|
||||
// console.log("Swallowed write operation during replay: " + name);
|
||||
}
|
||||
|
||||
@@ -322,13 +322,17 @@ namespace Playback {
|
||||
const wrapper: PlaybackIO = <any>{};
|
||||
initWrapper(wrapper, underlying);
|
||||
|
||||
wrapper.directoryName = (path): string => { throw new Error("NotSupported"); };
|
||||
wrapper.createDirectory = (path): void => { throw new Error("NotSupported"); };
|
||||
wrapper.directoryExists = (path): boolean => { throw new Error("NotSupported"); };
|
||||
wrapper.deleteFile = (path): void => { throw new Error("NotSupported"); };
|
||||
wrapper.listFiles = (path, filter, options): string[] => { throw new Error("NotSupported"); };
|
||||
wrapper.directoryName = notSupported;
|
||||
wrapper.createDirectory = notSupported;
|
||||
wrapper.directoryExists = notSupported;
|
||||
wrapper.deleteFile = notSupported;
|
||||
wrapper.listFiles = notSupported;
|
||||
|
||||
return wrapper;
|
||||
|
||||
function notSupported(): never {
|
||||
throw new Error("NotSupported");
|
||||
}
|
||||
}
|
||||
|
||||
export function wrapSystem(underlying: ts.System): PlaybackSystem {
|
||||
|
||||
@@ -178,7 +178,7 @@ class ProjectRunner extends RunnerBase {
|
||||
function createCompilerHost(): ts.CompilerHost {
|
||||
return {
|
||||
getSourceFile,
|
||||
getDefaultLibFileName: options => Harness.Compiler.defaultLibFileName,
|
||||
getDefaultLibFileName: () => Harness.Compiler.defaultLibFileName,
|
||||
writeFile,
|
||||
getCurrentDirectory,
|
||||
getCanonicalFileName: Harness.Compiler.getCanonicalFileName,
|
||||
@@ -421,7 +421,7 @@ class ProjectRunner extends RunnerBase {
|
||||
return undefined;
|
||||
}
|
||||
|
||||
function writeFile(fileName: string, data: string, writeByteOrderMark: boolean) {
|
||||
function writeFile() {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -61,6 +61,10 @@ function createRunner(kind: TestRunnerKind): RunnerBase {
|
||||
}
|
||||
}
|
||||
|
||||
if (Harness.IO.tryEnableSourceMapsForHost && /^development$/i.test(Harness.IO.getEnvironmentVariable("NODE_ENV"))) {
|
||||
Harness.IO.tryEnableSourceMapsForHost();
|
||||
}
|
||||
|
||||
// users can define tests to run in mytest.config that will override cmd line args, otherwise use cmd line args (test.config), otherwise no options
|
||||
|
||||
const mytestconfigFileName = "mytest.config";
|
||||
@@ -222,5 +226,5 @@ else {
|
||||
}
|
||||
if (!runUnitTests) {
|
||||
// patch `describe` to skip unit tests
|
||||
describe = <any>(function () { });
|
||||
describe = ts.noop as any;
|
||||
}
|
||||
|
||||
@@ -32,7 +32,7 @@ abstract class RunnerBase {
|
||||
/** Replaces instances of full paths with fileNames only */
|
||||
static removeFullPaths(path: string) {
|
||||
// If its a full path (starts with "C:" or "/") replace with just the filename
|
||||
let fixedPath = /^(\w:|\/)/.test(path) ? Harness.Path.getFileName(path) : path;
|
||||
let fixedPath = /^(\w:|\/)/.test(path) ? ts.getBaseFileName(path) : path;
|
||||
|
||||
// when running in the browser the 'full path' is the host name, shows up in error baselines
|
||||
const localHost = /http:\/localhost:\d+/g;
|
||||
|
||||
@@ -199,7 +199,7 @@ namespace RWC {
|
||||
}
|
||||
// Do not include the library in the baselines to avoid noise
|
||||
const baselineFiles = inputFiles.concat(otherFiles).filter(f => !Harness.isDefaultLibraryFile(f.unitName));
|
||||
const errors = compilerResult.errors.filter(e => !Harness.isDefaultLibraryFile(e.file.fileName));
|
||||
const errors = compilerResult.errors.filter(e => e.file && !Harness.isDefaultLibraryFile(e.file.fileName));
|
||||
return Harness.Compiler.getErrorBaseline(baselineFiles, errors);
|
||||
}, baselineOpts);
|
||||
});
|
||||
|
||||
@@ -10,7 +10,10 @@
|
||||
"stripInternal": true,
|
||||
"types": [
|
||||
"node", "mocha", "chai"
|
||||
]
|
||||
],
|
||||
"target": "es5",
|
||||
"noUnusedLocals": true,
|
||||
"noUnusedParameters": true
|
||||
},
|
||||
"files": [
|
||||
"../compiler/core.ts",
|
||||
@@ -26,13 +29,17 @@
|
||||
"../compiler/visitor.ts",
|
||||
"../compiler/transformers/ts.ts",
|
||||
"../compiler/transformers/jsx.ts",
|
||||
"../compiler/transformers/es7.ts",
|
||||
"../compiler/transformers/es6.ts",
|
||||
"../compiler/transformers/esnext.ts",
|
||||
"../compiler/transformers/es2017.ts",
|
||||
"../compiler/transformers/es2016.ts",
|
||||
"../compiler/transformers/es2015.ts",
|
||||
"../compiler/transformers/es5.ts",
|
||||
"../compiler/transformers/generators.ts",
|
||||
"../compiler/transformers/es5.ts",
|
||||
"../compiler/transformers/destructuring.ts",
|
||||
"../compiler/transformers/module/module.ts",
|
||||
"../compiler/transformers/module/system.ts",
|
||||
"../compiler/transformers/module/es6.ts",
|
||||
"../compiler/transformers/module/es2015.ts",
|
||||
"../compiler/transformer.ts",
|
||||
"../compiler/comments.ts",
|
||||
"../compiler/sourcemap.ts",
|
||||
@@ -101,7 +108,7 @@
|
||||
"./unittests/commandLineParsing.ts",
|
||||
"./unittests/configurationExtension.ts",
|
||||
"./unittests/convertCompilerOptionsFromJson.ts",
|
||||
"./unittests/convertTypingOptionsFromJson.ts",
|
||||
"./unittests/convertTypeAcquisitionFromJson.ts",
|
||||
"./unittests/tsserverProjectSystem.ts",
|
||||
"./unittests/matchFiles.ts",
|
||||
"./unittests/initializeTSConfig.ts",
|
||||
|
||||
@@ -21,65 +21,43 @@ namespace ts {
|
||||
args: <string[]>[],
|
||||
newLine: "\r\n",
|
||||
useCaseSensitiveFileNames: false,
|
||||
write: (s: string) => {
|
||||
},
|
||||
readFile: (path: string, encoding?: string): string => {
|
||||
return path in fileMap ? fileMap[path].content : undefined;
|
||||
},
|
||||
writeFile: (path: string, data: string, writeByteOrderMark?: boolean) => {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
resolvePath: (path: string): string => {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
fileExists: (path: string): boolean => {
|
||||
return path in fileMap;
|
||||
},
|
||||
directoryExists: (path: string): boolean => {
|
||||
return existingDirectories[path] || false;
|
||||
},
|
||||
createDirectory: (path: string) => {
|
||||
},
|
||||
getExecutingFilePath: (): string => {
|
||||
return "";
|
||||
},
|
||||
getCurrentDirectory: (): string => {
|
||||
return "";
|
||||
},
|
||||
getDirectories: (path: string) => [],
|
||||
getEnvironmentVariable: (name: string) => "",
|
||||
readDirectory: (path: string, extension?: string[], exclude?: string[], include?: string[]): string[] => {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
exit: (exitCode?: number) => {
|
||||
},
|
||||
watchFile: (path, callback) => {
|
||||
return {
|
||||
close: () => { }
|
||||
};
|
||||
},
|
||||
watchDirectory: (path, callback, recursive?) => {
|
||||
return {
|
||||
close: () => { }
|
||||
};
|
||||
},
|
||||
write: noop,
|
||||
readFile: path => path in fileMap ? fileMap[path].content : undefined,
|
||||
writeFile: notImplemented,
|
||||
resolvePath: notImplemented,
|
||||
fileExists: path => path in fileMap,
|
||||
directoryExists: path => existingDirectories[path] || false,
|
||||
createDirectory: noop,
|
||||
getExecutingFilePath: () => "",
|
||||
getCurrentDirectory: () => "",
|
||||
getDirectories: () => [],
|
||||
getEnvironmentVariable: () => "",
|
||||
readDirectory: notImplemented,
|
||||
exit: noop,
|
||||
watchFile: () => ({
|
||||
close: noop
|
||||
}),
|
||||
watchDirectory: () => ({
|
||||
close: noop
|
||||
}),
|
||||
setTimeout,
|
||||
clearTimeout,
|
||||
setImmediate,
|
||||
clearImmediate
|
||||
setImmediate: typeof setImmediate !== "undefined" ? setImmediate : action => setTimeout(action, 0),
|
||||
clearImmediate: typeof clearImmediate !== "undefined" ? clearImmediate : clearTimeout,
|
||||
createHash: s => s
|
||||
};
|
||||
}
|
||||
|
||||
function createProject(rootFile: string, serverHost: server.ServerHost): { project: server.Project, rootScriptInfo: server.ScriptInfo } {
|
||||
const logger: server.Logger = {
|
||||
close() { },
|
||||
close: noop,
|
||||
hasLevel: () => false,
|
||||
loggingEnabled: () => false,
|
||||
perftrc: (s: string) => { },
|
||||
info: (s: string) => { },
|
||||
startGroup: () => { },
|
||||
endGroup: () => { },
|
||||
msg: (s: string, type?: string) => { },
|
||||
perftrc: noop,
|
||||
info: noop,
|
||||
startGroup: noop,
|
||||
endGroup: noop,
|
||||
msg: noop,
|
||||
getLogFileName: (): string => undefined
|
||||
};
|
||||
|
||||
@@ -116,10 +94,7 @@ namespace ts {
|
||||
const originalFileExists = serverHost.fileExists;
|
||||
{
|
||||
// patch fileExists to make sure that disk is not touched
|
||||
serverHost.fileExists = (fileName): boolean => {
|
||||
assert.isTrue(false, "fileExists should not be called");
|
||||
return false;
|
||||
};
|
||||
serverHost.fileExists = notImplemented;
|
||||
|
||||
const newContent = `import {x} from "f1"
|
||||
var x: string = 1;`;
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\harness.ts" />
|
||||
/// <reference path="..\..\compiler\commandLineParser.ts" />
|
||||
|
||||
namespace ts {
|
||||
@@ -60,7 +60,7 @@ namespace ts {
|
||||
assertParseResult(["--lib", "es5,invalidOption", "0.ts"],
|
||||
{
|
||||
errors: [{
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
@@ -87,7 +87,7 @@ namespace ts {
|
||||
start: undefined,
|
||||
length: undefined,
|
||||
}, {
|
||||
messageText: "Argument for '--jsx' option must be: 'preserve', 'react'",
|
||||
messageText: "Argument for '--jsx' option must be: 'preserve', 'react'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
@@ -113,7 +113,7 @@ namespace ts {
|
||||
start: undefined,
|
||||
length: undefined,
|
||||
}, {
|
||||
messageText: "Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'",
|
||||
messageText: "Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
@@ -139,7 +139,7 @@ namespace ts {
|
||||
start: undefined,
|
||||
length: undefined,
|
||||
}, {
|
||||
messageText: "Argument for '--newLine' option must be: 'crlf', 'lf'",
|
||||
messageText: "Argument for '--newLine' option must be: 'crlf', 'lf'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
@@ -165,7 +165,7 @@ namespace ts {
|
||||
start: undefined,
|
||||
length: undefined,
|
||||
}, {
|
||||
messageText: "Argument for '--target' option must be: 'es3', 'es5', 'es6', 'es2015'",
|
||||
messageText: "Argument for '--target' option must be: 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'esnext'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
@@ -191,7 +191,7 @@ namespace ts {
|
||||
start: undefined,
|
||||
length: undefined,
|
||||
}, {
|
||||
messageText: "Argument for '--moduleResolution' option must be: 'node', 'classic'",
|
||||
messageText: "Argument for '--moduleResolution' option must be: 'node', 'classic'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
@@ -263,7 +263,7 @@ namespace ts {
|
||||
assertParseResult(["--lib", "es5,", "es7", "0.ts"],
|
||||
{
|
||||
errors: [{
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
@@ -283,7 +283,7 @@ namespace ts {
|
||||
assertParseResult(["--lib", "es5, ", "es7", "0.ts"],
|
||||
{
|
||||
errors: [{
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string'",
|
||||
category: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.category,
|
||||
code: ts.Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
|
||||
@@ -338,5 +338,38 @@ namespace ts {
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("Parse explicit boolean flag value", () => {
|
||||
assertParseResult(["--strictNullChecks", "false", "0.ts"],
|
||||
{
|
||||
errors: [],
|
||||
fileNames: ["0.ts"],
|
||||
options: {
|
||||
strictNullChecks: false,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("Parse non boolean argument after boolean flag", () => {
|
||||
assertParseResult(["--noImplicitAny", "t", "0.ts"],
|
||||
{
|
||||
errors: [],
|
||||
fileNames: ["t", "0.ts"],
|
||||
options: {
|
||||
noImplicitAny: true,
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
it("Parse implicit boolean flag value", () => {
|
||||
assertParseResult(["--strictNullChecks"],
|
||||
{
|
||||
errors: [],
|
||||
fileNames: [],
|
||||
options: {
|
||||
strictNullChecks: true,
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -467,6 +467,36 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
describe("EmitFile test", () => {
|
||||
it("should respect line endings", () => {
|
||||
test("\n");
|
||||
test("\r\n");
|
||||
|
||||
function test(newLine: string) {
|
||||
const lines = ["var x = 1;", "var y = 2;"];
|
||||
const path = "/a/app";
|
||||
const f = {
|
||||
path: path + ".ts",
|
||||
content: lines.join(newLine)
|
||||
};
|
||||
const host = createServerHost([f], { newLine });
|
||||
const session = createSession(host);
|
||||
session.executeCommand(<server.protocol.OpenRequest>{
|
||||
seq: 1,
|
||||
type: "request",
|
||||
command: "open",
|
||||
arguments: { file: f.path }
|
||||
});
|
||||
session.executeCommand(<server.protocol.CompileOnSaveEmitFileRequest>{
|
||||
seq: 2,
|
||||
type: "request",
|
||||
command: "compileOnSaveEmitFile",
|
||||
arguments: { file: f.path }
|
||||
});
|
||||
const emitOutput = host.readFile(path + ".js");
|
||||
assert.equal(emitOutput, f.content + newLine, "content of emit output should be identical with the input + newline");
|
||||
}
|
||||
})
|
||||
|
||||
it("should emit specified file", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/f1.ts",
|
||||
@@ -480,7 +510,7 @@ namespace ts.projectSystem {
|
||||
path: "/a/b/tsconfig.json",
|
||||
content: `{}`
|
||||
};
|
||||
const host = createServerHost([file1, file2, configFile, libFile]);
|
||||
const host = createServerHost([file1, file2, configFile, libFile], { newLine: "\r\n" });
|
||||
const typingsInstaller = createTestTypingsInstaller(host);
|
||||
const session = new server.Session(host, nullCancellationToken, /*useSingleInferredProject*/ false, typingsInstaller, Utils.byteLength, process.hrtime, nullLogger, /*canUseEvents*/ false);
|
||||
|
||||
@@ -492,5 +522,45 @@ namespace ts.projectSystem {
|
||||
assert.isTrue(host.fileExists(expectedEmittedFileName));
|
||||
assert.equal(host.readFile(expectedEmittedFileName), `"use strict";\r\nfunction Foo() { return 10; }\r\nexports.Foo = Foo;\r\n`);
|
||||
});
|
||||
|
||||
it("shoud not emit js files in external projects", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/file1.ts",
|
||||
content: "consonle.log('file1');"
|
||||
};
|
||||
// file2 has errors. The emitting should not be blocked.
|
||||
const file2 = {
|
||||
path: "/a/b/file2.js",
|
||||
content: "console.log'file2');"
|
||||
};
|
||||
const file3 = {
|
||||
path: "/a/b/file3.js",
|
||||
content: "console.log('file3');"
|
||||
};
|
||||
const externalProjectName = "externalproject";
|
||||
const host = createServerHost([file1, file2, file3, libFile]);
|
||||
const session = createSession(host);
|
||||
const projectService = session.getProjectService();
|
||||
|
||||
projectService.openExternalProject({
|
||||
rootFiles: toExternalFiles([file1.path, file2.path]),
|
||||
options: {
|
||||
allowJs: true,
|
||||
outFile: "dist.js",
|
||||
compileOnSave: true
|
||||
},
|
||||
projectFileName: externalProjectName
|
||||
});
|
||||
|
||||
const emitRequest = makeSessionRequest<server.protocol.CompileOnSaveEmitFileRequestArgs>(CommandNames.CompileOnSaveEmitFile, { file: file1.path });
|
||||
session.executeCommand(emitRequest);
|
||||
|
||||
const expectedOutFileName = "/a/b/dist.js";
|
||||
assert.isTrue(host.fileExists(expectedOutFileName));
|
||||
const outFileContent = host.readFile(expectedOutFileName);
|
||||
assert.isTrue(outFileContent.indexOf(file1.content) !== -1);
|
||||
assert.isTrue(outFileContent.indexOf(file2.content) === -1);
|
||||
assert.isTrue(outFileContent.indexOf(file3.content) === -1);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -179,7 +179,7 @@ namespace ts {
|
||||
testFailure("can error when 'extends' is neither relative nor rooted.", "extends2.json", [{
|
||||
code: 18001,
|
||||
category: DiagnosticCategory.Error,
|
||||
messageText: `The path in an 'extends' options must be relative or rooted.`
|
||||
messageText: `A path in an 'extends' option must be relative or rooted, but 'configs/base' is not.`
|
||||
}]);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -94,7 +94,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--jsx' option must be: 'preserve', 'react'",
|
||||
messageText: "Argument for '--jsx' option must be: 'preserve', 'react'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
@@ -122,7 +122,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'",
|
||||
messageText: "Argument for '--module' option must be: 'none', 'commonjs', 'amd', 'system', 'umd', 'es6', 'es2015'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
@@ -150,7 +150,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--newLine' option must be: 'crlf', 'lf'",
|
||||
messageText: "Argument for '--newLine' option must be: 'crlf', 'lf'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
@@ -176,7 +176,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--target' option must be: 'es3', 'es5', 'es6', 'es2015'",
|
||||
messageText: "Argument for '--target' option must be: 'es3', 'es5', 'es6', 'es2015', 'es2016', 'es2017', 'esnext'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
@@ -202,7 +202,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--moduleResolution' option must be: 'node', 'classic'",
|
||||
messageText: "Argument for '--moduleResolution' option must be: 'node', 'classic'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
@@ -233,7 +233,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
@@ -264,7 +264,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
@@ -295,7 +295,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
@@ -326,7 +326,7 @@ namespace ts {
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory'",
|
||||
messageText: "Argument for '--lib' option must be: 'es5', 'es6', 'es2015', 'es7', 'es2016', 'es2017', 'dom', 'dom.iterable', 'webworker', 'scripthost', 'es2015.core', 'es2015.collection', 'es2015.generator', 'es2015.iterable', 'es2015.promise', 'es2015.proxy', 'es2015.reflect', 'es2015.symbol', 'es2015.symbol.wellknown', 'es2016.array.include', 'es2017.object', 'es2017.sharedmemory', 'es2017.string'",
|
||||
code: Diagnostics.Argument_for_0_option_must_be_Colon_1.code,
|
||||
category: Diagnostics.Argument_for_0_option_must_be_Colon_1.category
|
||||
}]
|
||||
|
||||
+72
-49
@@ -2,12 +2,13 @@
|
||||
/// <reference path="..\..\compiler\commandLineParser.ts" />
|
||||
|
||||
namespace ts {
|
||||
describe("convertTypingOptionsFromJson", () => {
|
||||
function assertTypingOptions(json: any, configFileName: string, expectedResult: { typingOptions: TypingOptions, errors: Diagnostic[] }) {
|
||||
const { options: actualTypingOptions, errors: actualErrors } = convertTypingOptionsFromJson(json["typingOptions"], "/apath/", configFileName);
|
||||
const parsedTypingOptions = JSON.stringify(actualTypingOptions);
|
||||
const expectedTypingOptions = JSON.stringify(expectedResult.typingOptions);
|
||||
assert.equal(parsedTypingOptions, expectedTypingOptions);
|
||||
describe("convertTypeAcquisitionFromJson", () => {
|
||||
function assertTypeAcquisition(json: any, configFileName: string, expectedResult: { typeAcquisition: TypeAcquisition, errors: Diagnostic[] }) {
|
||||
const jsonOptions = json["typeAcquisition"] || json["typingOptions"];
|
||||
const { options: actualTypeAcquisition, errors: actualErrors } = convertTypeAcquisitionFromJson(jsonOptions, "/apath/", configFileName);
|
||||
const parsedTypeAcquisition = JSON.stringify(actualTypeAcquisition);
|
||||
const expectedTypeAcquisition = JSON.stringify(expectedResult.typeAcquisition);
|
||||
assert.equal(parsedTypeAcquisition, expectedTypeAcquisition);
|
||||
|
||||
const expectedErrors = expectedResult.errors;
|
||||
assert.isTrue(expectedResult.errors.length === actualErrors.length, `Expected error: ${JSON.stringify(expectedResult.errors)}. Actual error: ${JSON.stringify(actualErrors)}.`);
|
||||
@@ -20,8 +21,8 @@ namespace ts {
|
||||
}
|
||||
|
||||
// tsconfig.json
|
||||
it("Convert correctly format tsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert deprecated typingOptions.enableAutoDiscovery format tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
{
|
||||
@@ -32,9 +33,9 @@ namespace ts {
|
||||
},
|
||||
"tsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: true,
|
||||
enable: true,
|
||||
include: ["0.d.ts", "1.d.ts"],
|
||||
exclude: ["0.js", "1.js"]
|
||||
},
|
||||
@@ -42,25 +43,47 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert incorrect format tsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert correctly format tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enable": true,
|
||||
"include": ["0.d.ts", "1.d.ts"],
|
||||
"exclude": ["0.js", "1.js"]
|
||||
}
|
||||
},
|
||||
"tsconfig.json",
|
||||
{
|
||||
typeAcquisition:
|
||||
{
|
||||
enable: true,
|
||||
include: ["0.d.ts", "1.d.ts"],
|
||||
exclude: ["0.js", "1.js"]
|
||||
},
|
||||
errors: <Diagnostic[]>[]
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert incorrect format tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovy": true,
|
||||
}
|
||||
}, "tsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: false,
|
||||
enable: false,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
category: Diagnostics.Unknown_typing_option_0.category,
|
||||
code: Diagnostics.Unknown_typing_option_0.code,
|
||||
category: Diagnostics.Unknown_type_acquisition_option_0.category,
|
||||
code: Diagnostics.Unknown_type_acquisition_option_0.code,
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
@@ -70,12 +93,12 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert default tsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions({}, "tsconfig.json",
|
||||
it("Convert default tsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition({}, "tsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: false,
|
||||
enable: false,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
@@ -83,18 +106,18 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert tsconfig.json with only enableAutoDiscovery property to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert tsconfig.json with only enable property to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovery": true
|
||||
"enable": true
|
||||
}
|
||||
}, "tsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: true,
|
||||
enable: true,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
@@ -103,20 +126,20 @@ namespace ts {
|
||||
});
|
||||
|
||||
// jsconfig.json
|
||||
it("Convert jsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert jsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovery": false,
|
||||
"enable": false,
|
||||
"include": ["0.d.ts"],
|
||||
"exclude": ["0.js"]
|
||||
}
|
||||
}, "jsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: false,
|
||||
enable: false,
|
||||
include: ["0.d.ts"],
|
||||
exclude: ["0.js"]
|
||||
},
|
||||
@@ -124,12 +147,12 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert default jsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions({ }, "jsconfig.json",
|
||||
it("Convert default jsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition({ }, "jsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: true,
|
||||
enable: true,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
@@ -137,25 +160,25 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert incorrect format jsconfig.json to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert incorrect format jsconfig.json to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovy": true,
|
||||
}
|
||||
}, "jsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: true,
|
||||
enable: true,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
errors: [
|
||||
{
|
||||
category: Diagnostics.Unknown_compiler_option_0.category,
|
||||
code: Diagnostics.Unknown_typing_option_0.code,
|
||||
code: Diagnostics.Unknown_type_acquisition_option_0.code,
|
||||
file: undefined,
|
||||
start: 0,
|
||||
length: 0,
|
||||
@@ -165,18 +188,18 @@ namespace ts {
|
||||
});
|
||||
});
|
||||
|
||||
it("Convert jsconfig.json with only enableAutoDiscovery property to typing-options ", () => {
|
||||
assertTypingOptions(
|
||||
it("Convert jsconfig.json with only enable property to typeAcquisition ", () => {
|
||||
assertTypeAcquisition(
|
||||
{
|
||||
"typingOptions":
|
||||
"typeAcquisition":
|
||||
{
|
||||
"enableAutoDiscovery": false
|
||||
"enable": false
|
||||
}
|
||||
}, "jsconfig.json",
|
||||
{
|
||||
typingOptions:
|
||||
typeAcquisition:
|
||||
{
|
||||
enableAutoDiscovery: false,
|
||||
enable: false,
|
||||
include: [],
|
||||
exclude: []
|
||||
},
|
||||
@@ -28,7 +28,7 @@ namespace ts {
|
||||
const diagnostics2 = file2.parseDiagnostics;
|
||||
|
||||
assert.equal(diagnostics1.length, diagnostics2.length, "diagnostics1.length !== diagnostics2.length");
|
||||
for (let i = 0, n = diagnostics1.length; i < n; i++) {
|
||||
for (let i = 0; i < diagnostics1.length; i++) {
|
||||
const d1 = diagnostics1[i];
|
||||
const d2 = diagnostics2[i];
|
||||
|
||||
@@ -44,10 +44,10 @@ namespace ts {
|
||||
|
||||
// NOTE: 'reusedElements' is the expected count of elements reused from the old tree to the new
|
||||
// tree. It may change as we tweak the parser. If the count increases then that should always
|
||||
// be a good thing. If it decreases, that's not great (less reusability), but that may be
|
||||
// unavoidable. If it does decrease an investigation should be done to make sure that things
|
||||
// be a good thing. If it decreases, that's not great (less reusability), but that may be
|
||||
// unavoidable. If it does decrease an investigation should be done to make sure that things
|
||||
// are still ok and we're still appropriately reusing most of the tree.
|
||||
function compareTrees(oldText: IScriptSnapshot, newText: IScriptSnapshot, textChangeRange: TextChangeRange, expectedReusedElements: number, oldTree?: SourceFile): SourceFile {
|
||||
function compareTrees(oldText: IScriptSnapshot, newText: IScriptSnapshot, textChangeRange: TextChangeRange, expectedReusedElements: number, oldTree?: SourceFile) {
|
||||
oldTree = oldTree || createTree(oldText, /*version:*/ ".");
|
||||
Utils.assertInvariants(oldTree, /*parent:*/ undefined);
|
||||
|
||||
@@ -76,7 +76,7 @@ namespace ts {
|
||||
assert.equal(actualReusedCount, expectedReusedElements, actualReusedCount + " !== " + expectedReusedElements);
|
||||
}
|
||||
|
||||
return incrementalNewTree;
|
||||
return { oldTree, newTree, incrementalNewTree };
|
||||
}
|
||||
|
||||
function reusedElements(oldNode: SourceFile, newNode: SourceFile): number {
|
||||
@@ -103,7 +103,7 @@ namespace ts {
|
||||
for (let i = 0; i < repeat; i++) {
|
||||
const oldText = ScriptSnapshot.fromString(source);
|
||||
const newTextAndChange = withDelete(oldText, index, 1);
|
||||
const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree);
|
||||
const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree).incrementalNewTree;
|
||||
|
||||
source = newTextAndChange.text.getText(0, newTextAndChange.text.getLength());
|
||||
oldTree = newTree;
|
||||
@@ -116,7 +116,7 @@ namespace ts {
|
||||
for (let i = 0; i < repeat; i++) {
|
||||
const oldText = ScriptSnapshot.fromString(source);
|
||||
const newTextAndChange = withInsert(oldText, index + i, toInsert.charAt(i));
|
||||
const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree);
|
||||
const newTree = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1, oldTree).incrementalNewTree;
|
||||
|
||||
source = newTextAndChange.text.getText(0, newTextAndChange.text.getLength());
|
||||
oldTree = newTree;
|
||||
@@ -639,7 +639,7 @@ module m3 { }\
|
||||
});
|
||||
|
||||
it("Unterminated comment after keyword converted to identifier", () => {
|
||||
// 'public' as a keyword should be incrementally unusable (because it has an
|
||||
// 'public' as a keyword should be incrementally unusable (because it has an
|
||||
// unterminated comment). When we convert it to an identifier, that shouldn't
|
||||
// change anything, and we should still get the same errors.
|
||||
const source = "return; a.public /*";
|
||||
@@ -796,6 +796,16 @@ module m3 { }\
|
||||
compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, 4);
|
||||
});
|
||||
|
||||
it("Reuse transformFlags of subtree during bind", () => {
|
||||
const source = `class Greeter { constructor(element: HTMLElement) { } }`;
|
||||
const oldText = ScriptSnapshot.fromString(source);
|
||||
const newTextAndChange = withChange(oldText, 15, 0, "\n");
|
||||
const { oldTree, incrementalNewTree } = compareTrees(oldText, newTextAndChange.text, newTextAndChange.textChangeRange, -1);
|
||||
bindSourceFile(oldTree, {});
|
||||
bindSourceFile(incrementalNewTree, {});
|
||||
assert.equal(oldTree.transformFlags, incrementalNewTree.transformFlags);
|
||||
});
|
||||
|
||||
// Simulated typing tests.
|
||||
|
||||
it("Type extends clause 1", () => {
|
||||
|
||||
@@ -101,7 +101,7 @@ namespace ts {
|
||||
|
||||
Harness.Baseline.runBaseline("JSDocParsing/DocComments.parsesCorrectly." + name + ".json",
|
||||
() => JSON.stringify(comment.jsDoc,
|
||||
(k, v) => v && v.pos !== undefined ? JSON.parse(Utils.sourceFileToJSON(v)) : v, 4));
|
||||
(_, v) => v && v.pos !== undefined ? JSON.parse(Utils.sourceFileToJSON(v)) : v, 4));
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
+155
-181
@@ -3,6 +3,7 @@
|
||||
|
||||
namespace ts {
|
||||
const caseInsensitiveBasePath = "c:/dev/";
|
||||
const caseInsensitiveTsconfigPath = "c:/dev/tsconfig.json";
|
||||
const caseInsensitiveHost = new Utils.MockParseConfigHost(caseInsensitiveBasePath, /*useCaseSensitiveFileNames*/ false, [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/a.d.ts",
|
||||
@@ -88,7 +89,30 @@ namespace ts {
|
||||
"c:/dev/g.min.js/.g/g.ts"
|
||||
]);
|
||||
|
||||
function assertParsed(actual: ts.ParsedCommandLine, expected: ts.ParsedCommandLine): void {
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
}
|
||||
|
||||
describe("matchFiles", () => {
|
||||
it("with defaults", () => {
|
||||
const json = {};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/b.ts"
|
||||
],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
|
||||
describe("with literal file list", () => {
|
||||
it("without exclusions", () => {
|
||||
const json = {
|
||||
@@ -107,9 +131,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("missing files are still present", () => {
|
||||
const json = {
|
||||
@@ -128,9 +150,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("are not removed due to excludes", () => {
|
||||
const json = {
|
||||
@@ -152,9 +172,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -176,9 +194,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with non .ts file extensions are excluded", () => {
|
||||
const json = {
|
||||
@@ -189,14 +205,15 @@ namespace ts {
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with missing files are excluded", () => {
|
||||
const json = {
|
||||
@@ -207,14 +224,15 @@ namespace ts {
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with literal excludes", () => {
|
||||
const json = {
|
||||
@@ -235,9 +253,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with wildcard excludes", () => {
|
||||
const json = {
|
||||
@@ -265,9 +281,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with recursive excludes", () => {
|
||||
const json = {
|
||||
@@ -294,9 +308,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with case sensitive exclude", () => {
|
||||
const json = {
|
||||
@@ -316,9 +328,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseSensitiveHost, caseSensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with common package folders and no exclusions", () => {
|
||||
const json = {
|
||||
@@ -335,14 +345,15 @@ namespace ts {
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
"c:/dev/b.ts"
|
||||
"c:/dev/b.ts",
|
||||
"c:/dev/bower_components/a.ts",
|
||||
"c:/dev/jspm_packages/a.ts",
|
||||
"c:/dev/node_modules/a.ts"
|
||||
],
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with common package folders and exclusions", () => {
|
||||
const json = {
|
||||
@@ -369,9 +380,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with common package folders and empty exclude", () => {
|
||||
const json = {
|
||||
@@ -381,8 +390,7 @@ namespace ts {
|
||||
"node_modules/a.ts",
|
||||
"bower_components/a.ts",
|
||||
"jspm_packages/a.ts"
|
||||
],
|
||||
exclude: <string[]>[]
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
@@ -397,9 +405,7 @@ namespace ts {
|
||||
wildcardDirectories: {},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -423,9 +429,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("`*` matches only ts files", () => {
|
||||
const json = {
|
||||
@@ -446,9 +450,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("`?` matches only a single character", () => {
|
||||
const json = {
|
||||
@@ -468,9 +470,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with recursive directory", () => {
|
||||
const json = {
|
||||
@@ -492,9 +492,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with multiple recursive directories", () => {
|
||||
const json = {
|
||||
@@ -518,9 +516,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("case sensitive", () => {
|
||||
const json = {
|
||||
@@ -539,9 +535,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseSensitiveHost, caseSensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with missing files are excluded", () => {
|
||||
const json = {
|
||||
@@ -551,16 +545,17 @@ namespace ts {
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {
|
||||
"c:/dev": ts.WatchDirectoryFlags.Recursive
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("always include literal files", () => {
|
||||
const json = {
|
||||
@@ -585,9 +580,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("exclude folders", () => {
|
||||
const json = {
|
||||
@@ -612,30 +605,29 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with common package folders and no exclusions", () => {
|
||||
const json = {
|
||||
include: [
|
||||
"**/a.ts"
|
||||
"**/a.ts"
|
||||
]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts"
|
||||
"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
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with common package folders and exclusions", () => {
|
||||
const json = {
|
||||
@@ -659,9 +651,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with common package folders and empty exclude", () => {
|
||||
const json = {
|
||||
@@ -684,9 +674,7 @@ namespace ts {
|
||||
},
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveCommonFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("exclude .js files when allowJs=false", () => {
|
||||
const json = {
|
||||
@@ -701,16 +689,17 @@ namespace ts {
|
||||
options: {
|
||||
allowJs: false
|
||||
},
|
||||
errors: [],
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {
|
||||
"c:/dev/js": ts.WatchDirectoryFlags.None
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("include .js files when allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -735,9 +724,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("include explicitly listed .min.js files when allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -762,9 +749,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("include paths outside of the project", () => {
|
||||
const json = {
|
||||
@@ -788,9 +773,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("include paths outside of the project using relative paths", () => {
|
||||
const json = {
|
||||
@@ -813,9 +796,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("exclude paths outside of the project using relative paths", () => {
|
||||
const json = {
|
||||
@@ -828,14 +809,15 @@ namespace ts {
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude))]
|
||||
,
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("include files with .. in their name", () => {
|
||||
const json = {
|
||||
@@ -855,9 +837,7 @@ namespace ts {
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("exclude files with .. in their name", () => {
|
||||
const json = {
|
||||
@@ -879,9 +859,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with jsx=none, allowJs=false", () => {
|
||||
const json = {
|
||||
@@ -904,9 +882,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with jsx=preserve, allowJs=false", () => {
|
||||
const json = {
|
||||
@@ -931,9 +907,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with jsx=none, allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -958,9 +932,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with jsx=preserve, allowJs=true", () => {
|
||||
const json = {
|
||||
@@ -987,9 +959,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveMixedExtensionHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("exclude .min.js files using wildcards", () => {
|
||||
const json = {
|
||||
@@ -1016,9 +986,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
describe("with trailing recursive directory", () => {
|
||||
it("in includes", () => {
|
||||
@@ -1030,15 +998,15 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**")
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_end_in_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("in excludes", () => {
|
||||
const json = {
|
||||
@@ -1051,14 +1019,15 @@ namespace ts {
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude))
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
});
|
||||
describe("with multiple recursive directory patterns", () => {
|
||||
@@ -1071,15 +1040,15 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*")
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_multiple_recursive_directory_wildcards_Asterisk_Asterisk_Colon_0, "**/x/**/*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("in excludes", () => {
|
||||
const json = {
|
||||
@@ -1106,9 +1075,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1122,15 +1089,15 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*")
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/../*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
|
||||
it("in includes after a subdirectory", () => {
|
||||
@@ -1142,15 +1109,15 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*")
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/../*"),
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), "[]")
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
|
||||
it("in excludes immediately after", () => {
|
||||
@@ -1178,9 +1145,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
|
||||
it("in excludes after a subdirectory", () => {
|
||||
@@ -1195,7 +1160,7 @@ namespace ts {
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/..")
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.File_specification_cannot_contain_a_parent_directory_that_appears_after_a_recursive_directory_wildcard_Asterisk_Asterisk_Colon_0, "**/y/..")
|
||||
],
|
||||
fileNames: [
|
||||
"c:/dev/a.ts",
|
||||
@@ -1208,9 +1173,25 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with implicit globbification", () => {
|
||||
it("Expands 'z' to 'z/**/*'", () => {
|
||||
const json = {
|
||||
include: ["z"]
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
fileNames: [ "a.ts", "aba.ts", "abz.ts", "b.ts", "bba.ts", "bbz.ts" ].map(x => `c:/dev/z/${x}`),
|
||||
wildcardDirectories: {
|
||||
"c:/dev/z": ts.WatchDirectoryFlags.Recursive
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveHost, caseInsensitiveBasePath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -1235,9 +1216,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
describe("that are explicitly included", () => {
|
||||
it("without wildcards", () => {
|
||||
@@ -1257,9 +1236,7 @@ namespace ts {
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with recursive wildcards that match directories", () => {
|
||||
const json = {
|
||||
@@ -1281,9 +1258,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with recursive wildcards that match nothing", () => {
|
||||
const json = {
|
||||
@@ -1305,9 +1280,7 @@ namespace ts {
|
||||
}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
it("with wildcard excludes that implicitly exclude dotted files", () => {
|
||||
const json = {
|
||||
@@ -1320,14 +1293,15 @@ namespace ts {
|
||||
};
|
||||
const expected: ts.ParsedCommandLine = {
|
||||
options: {},
|
||||
errors: [],
|
||||
errors: [
|
||||
ts.createCompilerDiagnostic(ts.Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2,
|
||||
caseInsensitiveTsconfigPath, JSON.stringify(json.include), JSON.stringify(json.exclude))
|
||||
],
|
||||
fileNames: [],
|
||||
wildcardDirectories: {}
|
||||
};
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath);
|
||||
assert.deepEqual(actual.fileNames, expected.fileNames);
|
||||
assert.deepEqual(actual.wildcardDirectories, expected.wildcardDirectories);
|
||||
assert.deepEqual(actual.errors, expected.errors);
|
||||
const actual = ts.parseJsonConfigFileContent(json, caseInsensitiveDottedFoldersHost, caseInsensitiveBasePath, undefined, caseInsensitiveTsconfigPath);
|
||||
assertParsed(actual, expected);
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,6 +1,28 @@
|
||||
/// <reference path="..\harness.ts" />
|
||||
|
||||
namespace ts {
|
||||
export function checkResolvedModule(expected: ResolvedModuleFull, actual: ResolvedModuleFull): 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.extension === actual.extension, `'ext': expected '${Extension[expected.extension]}' to be equal to '${Extension[actual.extension]}'`);
|
||||
assert.isTrue(expected.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
export function checkResolvedModuleWithFailedLookupLocations(actual: ResolvedModuleWithFailedLookupLocations, expectedResolvedModule: ResolvedModuleFull, expectedFailedLookupLocations: string[]): void {
|
||||
assert.isTrue(actual.resolvedModule !== undefined, "module should be resolved");
|
||||
checkResolvedModule(actual.resolvedModule, expectedResolvedModule);
|
||||
assert.deepEqual(actual.failedLookupLocations, expectedFailedLookupLocations);
|
||||
}
|
||||
|
||||
export function createResolvedModule(resolvedFileName: string, isExternalLibraryImport = false): ResolvedModuleFull {
|
||||
return { resolvedFileName, extension: extensionFromPath(resolvedFileName), isExternalLibraryImport };
|
||||
}
|
||||
|
||||
interface File {
|
||||
name: string;
|
||||
content?: string;
|
||||
@@ -53,8 +75,7 @@ namespace ts {
|
||||
const containingFile = { name: containingFileName };
|
||||
const moduleFile = { name: moduleFileNameNoExt + ext };
|
||||
const resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
|
||||
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
|
||||
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
|
||||
checkResolvedModule(resolution.resolvedModule, createResolvedModule(moduleFile.name));
|
||||
|
||||
const failedLookupLocations: string[] = [];
|
||||
const dir = getDirectoryPath(containingFileName);
|
||||
@@ -97,8 +118,7 @@ namespace ts {
|
||||
const packageJson = { name: packageJsonFileName, content: JSON.stringify({ "typings": fieldRef }) };
|
||||
const moduleFile = { name: moduleFileName };
|
||||
const resolution = nodeModuleNameResolver(moduleName, containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, moduleFile));
|
||||
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
|
||||
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
|
||||
checkResolvedModule(resolution.resolvedModule, createResolvedModule(moduleFile.name));
|
||||
// expect three failed lookup location - attempt to load module as file with all supported extensions
|
||||
assert.equal(resolution.failedLookupLocations.length, supportedTypeScriptExtensions.length);
|
||||
}
|
||||
@@ -125,7 +145,7 @@ namespace ts {
|
||||
|
||||
const resolution = nodeModuleNameResolver("b", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, moduleFile, indexFile));
|
||||
|
||||
assert.equal(resolution.resolvedModule.resolvedFileName, indexPath);
|
||||
checkResolvedModule(resolution.resolvedModule, createResolvedModule(indexPath, /*isExternalLibraryImport*/true));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -138,7 +158,6 @@ namespace ts {
|
||||
/* tslint:enable no-null-keyword */
|
||||
testTypingsIgnored(undefined);
|
||||
});
|
||||
|
||||
it("module name as directory - load index.d.ts", () => {
|
||||
test(/*hasDirectoryExists*/ false);
|
||||
test(/*hasDirectoryExists*/ true);
|
||||
@@ -148,9 +167,7 @@ namespace ts {
|
||||
const packageJson = { name: "/a/b/foo/package.json", content: JSON.stringify({ main: "/c/d" }) };
|
||||
const indexFile = { name: "/a/b/foo/index.d.ts" };
|
||||
const resolution = nodeModuleNameResolver("./foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, packageJson, indexFile));
|
||||
assert.equal(resolution.resolvedModule.resolvedFileName, indexFile.name);
|
||||
assert.equal(!!resolution.resolvedModule.isExternalLibraryImport, false);
|
||||
assert.deepEqual(resolution.failedLookupLocations, [
|
||||
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(indexFile.name), [
|
||||
"/a/b/foo.ts",
|
||||
"/a/b/foo.tsx",
|
||||
"/a/b/foo.d.ts",
|
||||
@@ -170,35 +187,33 @@ namespace ts {
|
||||
const containingFile = { name: "/a/b/c/d/e.ts" };
|
||||
const moduleFile = { name: "/a/b/node_modules/foo.ts" };
|
||||
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
|
||||
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
|
||||
assert.deepEqual(resolution.failedLookupLocations, [
|
||||
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
|
||||
"/a/b/c/d/node_modules/foo.ts",
|
||||
"/a/b/c/d/node_modules/foo.tsx",
|
||||
"/a/b/c/d/node_modules/foo.d.ts",
|
||||
"/a/b/c/d/node_modules/foo/package.json",
|
||||
|
||||
"/a/b/c/d/node_modules/foo/index.ts",
|
||||
"/a/b/c/d/node_modules/foo/index.tsx",
|
||||
"/a/b/c/d/node_modules/foo/index.d.ts",
|
||||
"/a/b/c/d/node_modules/@types/foo.ts",
|
||||
"/a/b/c/d/node_modules/@types/foo.tsx",
|
||||
|
||||
"/a/b/c/d/node_modules/@types/foo.d.ts",
|
||||
"/a/b/c/d/node_modules/@types/foo/package.json",
|
||||
"/a/b/c/d/node_modules/@types/foo/index.ts",
|
||||
"/a/b/c/d/node_modules/@types/foo/index.tsx",
|
||||
|
||||
"/a/b/c/d/node_modules/@types/foo/index.d.ts",
|
||||
|
||||
"/a/b/c/node_modules/foo.ts",
|
||||
"/a/b/c/node_modules/foo.tsx",
|
||||
"/a/b/c/node_modules/foo.d.ts",
|
||||
"/a/b/c/node_modules/foo/package.json",
|
||||
|
||||
"/a/b/c/node_modules/foo/index.ts",
|
||||
"/a/b/c/node_modules/foo/index.tsx",
|
||||
"/a/b/c/node_modules/foo/index.d.ts",
|
||||
"/a/b/c/node_modules/@types/foo.ts",
|
||||
"/a/b/c/node_modules/@types/foo.tsx",
|
||||
|
||||
"/a/b/c/node_modules/@types/foo.d.ts",
|
||||
"/a/b/c/node_modules/@types/foo/package.json",
|
||||
"/a/b/c/node_modules/@types/foo/index.ts",
|
||||
"/a/b/c/node_modules/@types/foo/index.tsx",
|
||||
|
||||
"/a/b/c/node_modules/@types/foo/index.d.ts",
|
||||
]);
|
||||
}
|
||||
@@ -212,8 +227,7 @@ namespace ts {
|
||||
const containingFile = { name: "/a/b/c/d/e.ts" };
|
||||
const moduleFile = { name: "/a/b/node_modules/foo.d.ts" };
|
||||
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
|
||||
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
|
||||
assert.equal(resolution.resolvedModule.isExternalLibraryImport, true);
|
||||
checkResolvedModule(resolution.resolvedModule, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true));
|
||||
}
|
||||
});
|
||||
|
||||
@@ -225,55 +239,54 @@ namespace ts {
|
||||
const containingFile = { name: "/a/node_modules/b/c/node_modules/d/e.ts" };
|
||||
const moduleFile = { name: "/a/node_modules/foo/index.d.ts" };
|
||||
const resolution = nodeModuleNameResolver("foo", containingFile.name, {}, createModuleResolutionHost(hasDirectoryExists, containingFile, moduleFile));
|
||||
assert.equal(resolution.resolvedModule.resolvedFileName, moduleFile.name);
|
||||
assert.equal(resolution.resolvedModule.isExternalLibraryImport, true);
|
||||
assert.deepEqual(resolution.failedLookupLocations, [
|
||||
checkResolvedModuleWithFailedLookupLocations(resolution, createResolvedModule(moduleFile.name, /*isExternalLibraryImport*/ true), [
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo.tsx",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/package.json",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.tsx",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/foo/index.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.tsx",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/package.json",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.ts",
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.tsx",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/d/node_modules/@types/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/foo.ts",
|
||||
"/a/node_modules/b/c/node_modules/foo.tsx",
|
||||
"/a/node_modules/b/c/node_modules/foo.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/foo/package.json",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/foo/index.ts",
|
||||
"/a/node_modules/b/c/node_modules/foo/index.tsx",
|
||||
"/a/node_modules/b/c/node_modules/foo/index.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/@types/foo.ts",
|
||||
"/a/node_modules/b/c/node_modules/@types/foo.tsx",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/@types/foo.d.ts",
|
||||
"/a/node_modules/b/c/node_modules/@types/foo/package.json",
|
||||
"/a/node_modules/b/c/node_modules/@types/foo/index.ts",
|
||||
"/a/node_modules/b/c/node_modules/@types/foo/index.tsx",
|
||||
|
||||
"/a/node_modules/b/c/node_modules/@types/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/b/node_modules/foo.ts",
|
||||
"/a/node_modules/b/node_modules/foo.tsx",
|
||||
"/a/node_modules/b/node_modules/foo.d.ts",
|
||||
"/a/node_modules/b/node_modules/foo/package.json",
|
||||
|
||||
"/a/node_modules/b/node_modules/foo/index.ts",
|
||||
"/a/node_modules/b/node_modules/foo/index.tsx",
|
||||
"/a/node_modules/b/node_modules/foo/index.d.ts",
|
||||
"/a/node_modules/b/node_modules/@types/foo.ts",
|
||||
"/a/node_modules/b/node_modules/@types/foo.tsx",
|
||||
|
||||
"/a/node_modules/b/node_modules/@types/foo.d.ts",
|
||||
"/a/node_modules/b/node_modules/@types/foo/package.json",
|
||||
"/a/node_modules/b/node_modules/@types/foo/index.ts",
|
||||
"/a/node_modules/b/node_modules/@types/foo/index.tsx",
|
||||
|
||||
"/a/node_modules/b/node_modules/@types/foo/index.d.ts",
|
||||
|
||||
"/a/node_modules/foo.ts",
|
||||
"/a/node_modules/foo.tsx",
|
||||
"/a/node_modules/foo.d.ts",
|
||||
"/a/node_modules/foo/package.json",
|
||||
|
||||
"/a/node_modules/foo/index.ts",
|
||||
"/a/node_modules/foo/index.tsx"
|
||||
]);
|
||||
@@ -290,7 +303,7 @@ namespace ts {
|
||||
return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined;
|
||||
},
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
writeFile: (fileName, content): void => { throw new Error("NotImplemented"); },
|
||||
writeFile: notImplemented,
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
getDirectories: () => [],
|
||||
getCanonicalFileName: fileName => fileName.toLowerCase(),
|
||||
@@ -300,7 +313,7 @@ namespace ts {
|
||||
const path = normalizePath(combinePaths(currentDirectory, fileName));
|
||||
return path in files;
|
||||
},
|
||||
readFile: (fileName): string => { throw new Error("NotImplemented"); }
|
||||
readFile: notImplemented
|
||||
};
|
||||
|
||||
const program = createProgram(rootFiles, options, host);
|
||||
@@ -370,7 +383,7 @@ export = C;
|
||||
return path in files ? createSourceFile(fileName, files[path], languageVersion) : undefined;
|
||||
},
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
writeFile: (fileName, content): void => { throw new Error("NotImplemented"); },
|
||||
writeFile: notImplemented,
|
||||
getCurrentDirectory: () => currentDirectory,
|
||||
getDirectories: () => [],
|
||||
getCanonicalFileName,
|
||||
@@ -380,7 +393,7 @@ export = C;
|
||||
const path = getCanonicalFileName(normalizePath(combinePaths(currentDirectory, fileName)));
|
||||
return path in files;
|
||||
},
|
||||
readFile: (fileName): string => { throw new Error("NotImplemented"); }
|
||||
readFile: notImplemented
|
||||
};
|
||||
const program = createProgram(rootFiles, options, host);
|
||||
const diagnostics = sortAndDeduplicateDiagnostics(program.getSemanticDiagnostics().concat(program.getOptionsDiagnostics()));
|
||||
@@ -481,21 +494,15 @@ import b = require("./moduleB");
|
||||
const options: CompilerOptions = { moduleResolution, baseUrl: "/root" };
|
||||
{
|
||||
const result = resolveModuleName("folder2/file2", file1.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined, "module should be resolved");
|
||||
assert.equal(result.resolvedModule.resolvedFileName, file2.name);
|
||||
assert.deepEqual(result.failedLookupLocations, []);
|
||||
checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(file2.name), []);
|
||||
}
|
||||
{
|
||||
const result = resolveModuleName("./file3", file2.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined, "module should be resolved");
|
||||
assert.equal(result.resolvedModule.resolvedFileName, file3.name);
|
||||
assert.deepEqual(result.failedLookupLocations, []);
|
||||
checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(file3.name), []);
|
||||
}
|
||||
{
|
||||
const result = resolveModuleName("/root/folder1/file1", file2.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined, "module should be resolved");
|
||||
assert.equal(result.resolvedModule.resolvedFileName, file1.name);
|
||||
assert.deepEqual(result.failedLookupLocations, []);
|
||||
checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(file1.name), []);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -520,12 +527,11 @@ import b = require("./moduleB");
|
||||
check("m1", main, m1);
|
||||
check("m2", main, m2);
|
||||
check("m3", main, m3Typings);
|
||||
check("m4", main, m4);
|
||||
check("m4", main, m4, /*isExternalLibraryImport*/true);
|
||||
|
||||
function check(name: string, caller: File, expected: File) {
|
||||
function check(name: string, caller: File, expected: File, isExternalLibraryImport = false) {
|
||||
const result = resolveModuleName(name, caller.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined);
|
||||
assert.equal(result.resolvedModule.resolvedFileName, expected.name);
|
||||
checkResolvedModule(result.resolvedModule, createResolvedModule(expected.name, isExternalLibraryImport));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -547,8 +553,7 @@ import b = require("./moduleB");
|
||||
|
||||
function check(name: string, caller: File, expected: File) {
|
||||
const result = resolveModuleName(name, caller.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined);
|
||||
assert.equal(result.resolvedModule.resolvedFileName, expected.name);
|
||||
checkResolvedModule(result.resolvedModule, createResolvedModule(expected.name));
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -590,9 +595,10 @@ import b = require("./moduleB");
|
||||
"/root/folder1/file2.tsx",
|
||||
"/root/folder1/file2.d.ts",
|
||||
"/root/folder1/file2/package.json",
|
||||
|
||||
"/root/folder1/file2/index.ts",
|
||||
"/root/folder1/file2/index.tsx",
|
||||
"/root/folder1/file2/index.d.ts"
|
||||
"/root/folder1/file2/index.d.ts",
|
||||
// then first attempt on 'generated/*' was successful
|
||||
]);
|
||||
check("folder2/file3", file3, [
|
||||
@@ -601,14 +607,17 @@ import b = require("./moduleB");
|
||||
"/root/folder2/file3.tsx",
|
||||
"/root/folder2/file3.d.ts",
|
||||
"/root/folder2/file3/package.json",
|
||||
|
||||
"/root/folder2/file3/index.ts",
|
||||
"/root/folder2/file3/index.tsx",
|
||||
"/root/folder2/file3/index.d.ts",
|
||||
|
||||
// then use remapped location
|
||||
"/root/generated/folder2/file3.ts",
|
||||
"/root/generated/folder2/file3.tsx",
|
||||
"/root/generated/folder2/file3.d.ts",
|
||||
"/root/generated/folder2/file3/package.json",
|
||||
|
||||
"/root/generated/folder2/file3/index.ts",
|
||||
"/root/generated/folder2/file3/index.tsx",
|
||||
// success on index.d.ts
|
||||
@@ -619,13 +628,15 @@ import b = require("./moduleB");
|
||||
"/root/folder2/file4.tsx",
|
||||
"/root/folder2/file4.d.ts",
|
||||
"/root/folder2/file4/package.json",
|
||||
|
||||
"/root/folder2/file4/index.ts",
|
||||
"/root/folder2/file4/index.tsx",
|
||||
"/root/folder2/file4/index.d.ts",
|
||||
|
||||
// try to load from file from remapped location
|
||||
"/root/generated/folder2/file4.ts",
|
||||
"/root/generated/folder2/file4.tsx",
|
||||
"/root/generated/folder2/file4.d.ts"
|
||||
"/root/generated/folder2/file4.d.ts",
|
||||
// success on loading as from folder
|
||||
]);
|
||||
check("somefolder/file5", file5, [
|
||||
@@ -634,6 +645,7 @@ import b = require("./moduleB");
|
||||
"/root/someanotherfolder/file5.ts",
|
||||
"/root/someanotherfolder/file5.tsx",
|
||||
"/root/someanotherfolder/file5.d.ts",
|
||||
|
||||
// load from folder
|
||||
"/root/someanotherfolder/file5/package.json",
|
||||
"/root/someanotherfolder/file5/index.ts",
|
||||
@@ -646,46 +658,47 @@ import b = require("./moduleB");
|
||||
"/root/file6.ts",
|
||||
"/root/file6.tsx",
|
||||
"/root/file6.d.ts",
|
||||
|
||||
// load from folder
|
||||
"/root/file6/package.json",
|
||||
"/root/file6/index.ts",
|
||||
"/root/file6/index.tsx",
|
||||
"/root/file6/index.d.ts",
|
||||
|
||||
// then try 'generated/*'
|
||||
// load from file
|
||||
"/root/generated/file6.ts",
|
||||
"/root/generated/file6.tsx",
|
||||
"/root/generated/file6.d.ts",
|
||||
|
||||
// load from folder
|
||||
"/root/generated/file6/package.json",
|
||||
"/root/generated/file6/index.ts",
|
||||
"/root/generated/file6/index.tsx",
|
||||
"/root/generated/file6/index.d.ts",
|
||||
|
||||
// fallback to standard node behavior
|
||||
// load from file
|
||||
"/root/folder1/node_modules/file6.ts",
|
||||
"/root/folder1/node_modules/file6.tsx",
|
||||
"/root/folder1/node_modules/file6.d.ts",
|
||||
|
||||
// load from folder
|
||||
"/root/folder1/node_modules/file6/package.json",
|
||||
"/root/folder1/node_modules/file6/index.ts",
|
||||
"/root/folder1/node_modules/file6/index.tsx",
|
||||
"/root/folder1/node_modules/file6/index.d.ts",
|
||||
"/root/folder1/node_modules/@types/file6.ts",
|
||||
"/root/folder1/node_modules/@types/file6.tsx",
|
||||
"/root/folder1/node_modules/@types/file6.d.ts",
|
||||
"/root/folder1/node_modules/@types/file6/package.json",
|
||||
"/root/folder1/node_modules/@types/file6/index.ts",
|
||||
"/root/folder1/node_modules/@types/file6/index.tsx",
|
||||
"/root/folder1/node_modules/@types/file6/index.d.ts"
|
||||
// success on /root/node_modules/file6.ts
|
||||
]);
|
||||
|
||||
function check(name: string, expected: File, expectedFailedLookups: string[]) {
|
||||
"/root/folder1/node_modules/@types/file6.d.ts",
|
||||
|
||||
"/root/folder1/node_modules/@types/file6/package.json",
|
||||
"/root/folder1/node_modules/@types/file6/index.d.ts",
|
||||
// success on /root/node_modules/file6.ts
|
||||
], /*isExternalLibraryImport*/ true);
|
||||
|
||||
function check(name: string, expected: File, expectedFailedLookups: string[], isExternalLibraryImport = false) {
|
||||
const result = resolveModuleName(name, main.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined, "module should be resolved");
|
||||
assert.equal(result.resolvedModule.resolvedFileName, expected.name);
|
||||
assert.deepEqual(result.failedLookupLocations, expectedFailedLookups);
|
||||
checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(expected.name, isExternalLibraryImport), expectedFailedLookups);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -744,9 +757,7 @@ import b = require("./moduleB");
|
||||
|
||||
function check(name: string, expected: File, expectedFailedLookups: string[]) {
|
||||
const result = resolveModuleName(name, main.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined, "module should be resolved");
|
||||
assert.equal(result.resolvedModule.resolvedFileName, expected.name);
|
||||
assert.deepEqual(result.failedLookupLocations, expectedFailedLookups);
|
||||
checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(expected.name), expectedFailedLookups);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -819,9 +830,7 @@ import b = require("./moduleB");
|
||||
|
||||
function check(name: string, container: File, expected: File, expectedFailedLookups: string[]) {
|
||||
const result = resolveModuleName(name, container.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined, "module should be resolved");
|
||||
assert.equal(result.resolvedModule.resolvedFileName, expected.name);
|
||||
assert.deepEqual(result.failedLookupLocations, expectedFailedLookups);
|
||||
checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(expected.name), expectedFailedLookups);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -875,9 +884,7 @@ import b = require("./moduleB");
|
||||
|
||||
function check(name: string, container: File, expected: File, expectedFailedLookups: string[]) {
|
||||
const result = resolveModuleName(name, container.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined, "module should be resolved");
|
||||
assert.equal(result.resolvedModule.resolvedFileName, expected.name);
|
||||
assert.deepEqual(result.failedLookupLocations, expectedFailedLookups);
|
||||
checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(expected.name), expectedFailedLookups);
|
||||
}
|
||||
}
|
||||
});
|
||||
@@ -899,9 +906,7 @@ import b = require("./moduleB");
|
||||
}
|
||||
};
|
||||
const result = resolveModuleName("libs/guid", app.name, options, host);
|
||||
assert.isTrue(result.resolvedModule !== undefined, "module should be resolved");
|
||||
assert.equal(result.resolvedModule.resolvedFileName, libsTypings.name);
|
||||
assert.deepEqual(result.failedLookupLocations, [
|
||||
checkResolvedModuleWithFailedLookupLocations(result, createResolvedModule(libsTypings.name), [
|
||||
// first try to load module as file
|
||||
"/root/src/libs/guid.ts",
|
||||
"/root/src/libs/guid.tsx",
|
||||
@@ -911,15 +916,11 @@ import b = require("./moduleB");
|
||||
});
|
||||
});
|
||||
|
||||
function notImplemented(name: string): () => any {
|
||||
return () => assert(`${name} is not implemented and should not be called`);
|
||||
}
|
||||
|
||||
describe("ModuleResolutionHost.directoryExists", () => {
|
||||
it("No 'fileExists' calls if containing directory is missing", () => {
|
||||
const host: ModuleResolutionHost = {
|
||||
readFile: notImplemented("readFile"),
|
||||
fileExists: notImplemented("fileExists"),
|
||||
readFile: notImplemented,
|
||||
fileExists: notImplemented,
|
||||
directoryExists: _ => false
|
||||
};
|
||||
|
||||
@@ -1017,14 +1018,12 @@ import b = require("./moduleB");
|
||||
const files = [f1, f2, f3, f4];
|
||||
|
||||
const names = map(files, f => f.name);
|
||||
const sourceFiles = arrayToMap(map(files, f => createSourceFile(f.name, f.content, ScriptTarget.ES6)), f => f.fileName);
|
||||
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],
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
writeFile(file, text) {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
writeFile: notImplemented,
|
||||
getCurrentDirectory: () => "/",
|
||||
getDirectories: () => [],
|
||||
getCanonicalFileName: f => f.toLowerCase(),
|
||||
@@ -1042,5 +1041,69 @@ import b = require("./moduleB");
|
||||
assert.equal(diagnostics2.length, 1, "expected one diagnostic");
|
||||
assert.equal(diagnostics1[0].messageText, diagnostics2[0].messageText, "expected one diagnostic");
|
||||
});
|
||||
|
||||
it ("Modules in the same .d.ts file are preferred to external files", () => {
|
||||
const f = {
|
||||
name: "/a/b/c/c/app.d.ts",
|
||||
content: `
|
||||
declare module "fs" {
|
||||
export interface Stat { id: number }
|
||||
}
|
||||
declare module "fs-client" {
|
||||
import { Stat } from "fs";
|
||||
export function foo(): Stat;
|
||||
}`
|
||||
};
|
||||
const file = createSourceFile(f.name, f.content, ScriptTarget.ES2015);
|
||||
const compilerHost: CompilerHost = {
|
||||
fileExists : fileName => fileName === file.fileName,
|
||||
getSourceFile: fileName => fileName === file.fileName ? file : undefined,
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
writeFile: notImplemented,
|
||||
getCurrentDirectory: () => "/",
|
||||
getDirectories: () => [],
|
||||
getCanonicalFileName: f => f.toLowerCase(),
|
||||
getNewLine: () => "\r\n",
|
||||
useCaseSensitiveFileNames: () => false,
|
||||
readFile: fileName => fileName === file.fileName ? file.text : undefined,
|
||||
resolveModuleNames() {
|
||||
assert(false, "resolveModuleNames should not be called");
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
createProgram([f.name], {}, compilerHost);
|
||||
});
|
||||
|
||||
it ("Modules in .ts file are not checked in the same file", () => {
|
||||
const f = {
|
||||
name: "/a/b/c/c/app.ts",
|
||||
content: `
|
||||
declare module "fs" {
|
||||
export interface Stat { id: number }
|
||||
}
|
||||
declare module "fs-client" {
|
||||
import { Stat } from "fs";
|
||||
export function foo(): Stat;
|
||||
}`
|
||||
};
|
||||
const file = createSourceFile(f.name, f.content, ScriptTarget.ES2015);
|
||||
const compilerHost: CompilerHost = {
|
||||
fileExists : fileName => fileName === file.fileName,
|
||||
getSourceFile: fileName => fileName === file.fileName ? file : undefined,
|
||||
getDefaultLibFileName: () => "lib.d.ts",
|
||||
writeFile: notImplemented,
|
||||
getCurrentDirectory: () => "/",
|
||||
getDirectories: () => [],
|
||||
getCanonicalFileName: f => f.toLowerCase(),
|
||||
getNewLine: () => "\r\n",
|
||||
useCaseSensitiveFileNames: () => false,
|
||||
readFile: fileName => fileName === file.fileName ? file.text : undefined,
|
||||
resolveModuleNames(moduleNames: string[], _containingFile: string) {
|
||||
assert.deepEqual(moduleNames, ["fs"]);
|
||||
return [undefined];
|
||||
}
|
||||
};
|
||||
createProgram([f.name], {}, compilerHost);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -22,6 +22,11 @@ namespace ts {
|
||||
|
||||
interface ProgramWithSourceTexts extends Program {
|
||||
sourceTexts?: NamedSourceText[];
|
||||
host: TestCompilerHost;
|
||||
}
|
||||
|
||||
interface TestCompilerHost extends CompilerHost {
|
||||
getTrace(): string[];
|
||||
}
|
||||
|
||||
class SourceText implements IScriptSnapshot {
|
||||
@@ -101,23 +106,32 @@ namespace ts {
|
||||
return file;
|
||||
}
|
||||
|
||||
function createTestCompilerHost(texts: NamedSourceText[], target: ScriptTarget): CompilerHost {
|
||||
const files = arrayToMap(texts, t => t.name, t => createSourceFileWithText(t.name, t.text, target));
|
||||
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);
|
||||
if (oldFile && oldFile.sourceText.getVersion() === t.text.getVersion()) {
|
||||
return oldFile;
|
||||
}
|
||||
}
|
||||
return createSourceFileWithText(t.name, t.text, target);
|
||||
});
|
||||
const trace: string[] = [];
|
||||
|
||||
return {
|
||||
trace: s => trace.push(s),
|
||||
getTrace: () => trace,
|
||||
getSourceFile(fileName): SourceFile {
|
||||
return files[fileName];
|
||||
},
|
||||
getDefaultLibFileName(): string {
|
||||
return "lib.d.ts";
|
||||
},
|
||||
writeFile(file, text) {
|
||||
throw new Error("NYI");
|
||||
},
|
||||
writeFile: notImplemented,
|
||||
getCurrentDirectory(): string {
|
||||
return "";
|
||||
},
|
||||
getDirectories(path: string): string[] {
|
||||
getDirectories(): string[] {
|
||||
return [];
|
||||
},
|
||||
getCanonicalFileName(fileName): string {
|
||||
@@ -132,37 +146,28 @@ namespace ts {
|
||||
fileExists: fileName => fileName in files,
|
||||
readFile: fileName => {
|
||||
return fileName in files ? files[fileName].text : undefined;
|
||||
}
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function newProgram(texts: NamedSourceText[], rootNames: string[], options: CompilerOptions): Program {
|
||||
function newProgram(texts: NamedSourceText[], rootNames: string[], options: CompilerOptions): ProgramWithSourceTexts {
|
||||
const host = createTestCompilerHost(texts, options.target);
|
||||
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host);
|
||||
program.sourceTexts = texts;
|
||||
program.host = host;
|
||||
return program;
|
||||
}
|
||||
|
||||
function updateProgram(oldProgram: Program, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void) {
|
||||
function updateProgram(oldProgram: ProgramWithSourceTexts, rootNames: string[], options: CompilerOptions, updater: (files: NamedSourceText[]) => void) {
|
||||
const texts: NamedSourceText[] = (<ProgramWithSourceTexts>oldProgram).sourceTexts.slice(0);
|
||||
updater(texts);
|
||||
const host = createTestCompilerHost(texts, options.target);
|
||||
const host = createTestCompilerHost(texts, options.target, oldProgram);
|
||||
const program = <ProgramWithSourceTexts>createProgram(rootNames, options, host, oldProgram);
|
||||
program.sourceTexts = texts;
|
||||
program.host = host;
|
||||
return program;
|
||||
}
|
||||
|
||||
function checkResolvedModule(expected: ResolvedModule, actual: ResolvedModule): 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.isExternalLibraryImport === actual.isExternalLibraryImport, `'isExternalLibraryImport': expected '${expected.isExternalLibraryImport}' to be equal to '${actual.isExternalLibraryImport}'`);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function checkResolvedTypeDirective(expected: ResolvedTypeReferenceDirective, actual: ResolvedTypeReferenceDirective): boolean {
|
||||
if (!expected === !actual) {
|
||||
if (expected) {
|
||||
@@ -244,17 +249,13 @@ namespace ts {
|
||||
|
||||
it("fails if change affects type references", () => {
|
||||
const program_1 = newProgram(files, ["a.ts"], { types: ["a"] });
|
||||
updateProgram(program_1, ["a.ts"], { types: ["b"] }, files => {
|
||||
|
||||
});
|
||||
updateProgram(program_1, ["a.ts"], { types: ["b"] }, noop);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
});
|
||||
|
||||
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"] }, files => {
|
||||
|
||||
});
|
||||
updateProgram(program_1, ["a.ts"], { types: ["a"] }, noop);
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
});
|
||||
|
||||
@@ -280,19 +281,19 @@ namespace ts {
|
||||
|
||||
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 }, files => void 0);
|
||||
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.AMD }, noop);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
});
|
||||
|
||||
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" }, files => void 0);
|
||||
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, rootDir: "/a/c" }, noop);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
});
|
||||
|
||||
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" }, files => void 0);
|
||||
updateProgram(program_1, ["a.ts"], { target, module: ModuleKind.CommonJS, configFilePath: "/a/c/tsconfig.json" }, noop);
|
||||
assert.isTrue(!program_1.structureIsReused);
|
||||
});
|
||||
|
||||
@@ -306,7 +307,7 @@ namespace ts {
|
||||
const options: CompilerOptions = { target };
|
||||
|
||||
const program_1 = newProgram(files, ["a.ts"], options);
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" } }));
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": createResolvedModule("b.ts") }));
|
||||
checkResolvedModulesCache(program_1, "b.ts", undefined);
|
||||
|
||||
const program_2 = updateProgram(program_1, ["a.ts"], options, files => {
|
||||
@@ -315,7 +316,7 @@ namespace ts {
|
||||
assert.isTrue(program_1.structureIsReused);
|
||||
|
||||
// content of resolution cache should not change
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" } }));
|
||||
checkResolvedModulesCache(program_1, "a.ts", createMap({ "b": createResolvedModule("b.ts") }));
|
||||
checkResolvedModulesCache(program_1, "b.ts", undefined);
|
||||
|
||||
// imports has changed - program is not reused
|
||||
@@ -332,7 +333,7 @@ namespace ts {
|
||||
files[0].text = files[0].text.updateImportsAndExports(newImports);
|
||||
});
|
||||
assert.isTrue(!program_3.structureIsReused);
|
||||
checkResolvedModulesCache(program_4, "a.ts", createMap({ "b": { resolvedFileName: "b.ts" }, "c": undefined }));
|
||||
checkResolvedModulesCache(program_4, "a.ts", createMap({ "b": createResolvedModule("b.ts"), "c": undefined }));
|
||||
});
|
||||
|
||||
it("resolved type directives cache follows type directives", () => {
|
||||
@@ -372,6 +373,88 @@ namespace ts {
|
||||
assert.isTrue(!program_3.structureIsReused);
|
||||
checkResolvedTypeDirectivesCache(program_1, "/a.ts", createMap({ "typedefs": { resolvedFileName: "/types/typedefs/index.d.ts", primary: true } }));
|
||||
});
|
||||
|
||||
it("can reuse ambient module declarations from non-modified files", () => {
|
||||
const files = [
|
||||
{ name: "/a/b/app.ts", text: SourceText.New("", "import * as fs from 'fs'", "") },
|
||||
{ name: "/a/b/node.d.ts", text: SourceText.New("", "", "declare module 'fs' {}") }
|
||||
];
|
||||
const options = { target: ScriptTarget.ES2015, traceResolution: true };
|
||||
const program = newProgram(files, files.map(f => f.name), options);
|
||||
assert.deepEqual(program.host.getTrace(),
|
||||
[
|
||||
"======== Resolving module 'fs' from '/a/b/app.ts'. ========",
|
||||
"Module resolution kind is not specified, using 'Classic'.",
|
||||
"File '/a/b/fs.ts' does not exist.",
|
||||
"File '/a/b/fs.tsx' does not exist.",
|
||||
"File '/a/b/fs.d.ts' does not exist.",
|
||||
"File '/a/fs.ts' does not exist.",
|
||||
"File '/a/fs.tsx' does not exist.",
|
||||
"File '/a/fs.d.ts' does not exist.",
|
||||
"File '/fs.ts' does not exist.",
|
||||
"File '/fs.tsx' does not exist.",
|
||||
"File '/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/a/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/a/b/fs.js' does not exist.",
|
||||
"File '/a/b/fs.jsx' does not exist.",
|
||||
"File '/a/fs.js' does not exist.",
|
||||
"File '/a/fs.jsx' does not exist.",
|
||||
"File '/fs.js' does not exist.",
|
||||
"File '/fs.jsx' does not exist.",
|
||||
"======== Module name 'fs' was not resolved. ========",
|
||||
], "should look for 'fs'");
|
||||
|
||||
const program_2 = updateProgram(program, program.getRootFileNames(), options, f => {
|
||||
f[0].text = f[0].text.updateProgram("var x = 1;");
|
||||
});
|
||||
assert.deepEqual(program_2.host.getTrace(), [
|
||||
"Module 'fs' was resolved as ambient module declared in '/a/b/node.d.ts' since this file was not modified."
|
||||
], "should reuse 'fs' since node.d.ts was not changed");
|
||||
|
||||
const program_3 = updateProgram(program_2, program_2.getRootFileNames(), options, f => {
|
||||
f[0].text = f[0].text.updateProgram("var y = 1;");
|
||||
f[1].text = f[1].text.updateProgram("declare var process: any");
|
||||
});
|
||||
assert.deepEqual(program_3.host.getTrace(),
|
||||
[
|
||||
"======== Resolving module 'fs' from '/a/b/app.ts'. ========",
|
||||
"Module resolution kind is not specified, using 'Classic'.",
|
||||
"File '/a/b/fs.ts' does not exist.",
|
||||
"File '/a/b/fs.tsx' does not exist.",
|
||||
"File '/a/b/fs.d.ts' does not exist.",
|
||||
"File '/a/fs.ts' does not exist.",
|
||||
"File '/a/fs.tsx' does not exist.",
|
||||
"File '/a/fs.d.ts' does not exist.",
|
||||
"File '/fs.ts' does not exist.",
|
||||
"File '/fs.tsx' does not exist.",
|
||||
"File '/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/a/b/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/a/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/a/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs.d.ts' does not exist.",
|
||||
"File '/node_modules/@types/fs/package.json' does not exist.",
|
||||
"File '/node_modules/@types/fs/index.d.ts' does not exist.",
|
||||
"File '/a/b/fs.js' does not exist.",
|
||||
"File '/a/b/fs.jsx' does not exist.",
|
||||
"File '/a/fs.js' does not exist.",
|
||||
"File '/a/fs.jsx' does not exist.",
|
||||
"File '/fs.js' does not exist.",
|
||||
"File '/fs.jsx' does not exist.",
|
||||
"======== Module name 'fs' was not resolved. ========",
|
||||
], "should look for 'fs' again since node.d.ts was changed");
|
||||
|
||||
});
|
||||
});
|
||||
|
||||
describe("host is optional", () => {
|
||||
|
||||
@@ -13,8 +13,7 @@ describe("Colorization", function () {
|
||||
|
||||
function getEntryAtPosition(result: ts.ClassificationResult, position: number) {
|
||||
let entryPosition = 0;
|
||||
for (let i = 0, n = result.entries.length; i < n; i++) {
|
||||
const entry = result.entries[i];
|
||||
for (const entry of result.entries) {
|
||||
if (entryPosition === position) {
|
||||
return entry;
|
||||
}
|
||||
@@ -43,9 +42,7 @@ describe("Colorization", function () {
|
||||
function testLexicalClassification(text: string, initialEndOfLineState: ts.EndOfLineState, ...expectedEntries: ClassificationEntry[]): void {
|
||||
const result = classifier.getClassificationsForLine(text, initialEndOfLineState, /*syntacticClassifierAbsent*/ false);
|
||||
|
||||
for (let i = 0, n = expectedEntries.length; i < n; i++) {
|
||||
const expectedEntry = expectedEntries[i];
|
||||
|
||||
for (const expectedEntry of expectedEntries) {
|
||||
if (expectedEntry.classification === undefined) {
|
||||
assert.equal(result.finalLexState, expectedEntry.value, "final endOfLineState does not match expected.");
|
||||
}
|
||||
@@ -352,9 +349,9 @@ describe("Colorization", function () {
|
||||
// Adjusts 'pos' by accounting for the length of each portion of the string,
|
||||
// but only return the last given string
|
||||
function track(...vals: string[]): string {
|
||||
for (let i = 0, n = vals.length; i < n; i++) {
|
||||
for (const val of vals) {
|
||||
pos += lastLength;
|
||||
lastLength = vals[i].length;
|
||||
lastLength = val.length;
|
||||
}
|
||||
return ts.lastOrUndefined(vals);
|
||||
}
|
||||
|
||||
@@ -62,7 +62,7 @@ describe("DocumentRegistry", () => {
|
||||
const snapshot = ts.ScriptSnapshot.fromString(contents);
|
||||
|
||||
// Always treat any change as a full change.
|
||||
snapshot.getChangeRange = old => ts.createTextChangeRange(ts.createTextSpan(0, contents.length), contents.length);
|
||||
snapshot.getChangeRange = () => ts.createTextChangeRange(ts.createTextSpan(0, contents.length), contents.length);
|
||||
|
||||
// Simulate one LS getting the document.
|
||||
documentRegistry.acquireDocument("file1.ts", defaultCompilerOptions, snapshot, /* version */ "1");
|
||||
|
||||
@@ -502,7 +502,7 @@ describe("PatternMatcher", function () {
|
||||
function assertArrayEquals<T>(array1: T[], array2: T[]) {
|
||||
assert.equal(array1.length, array2.length);
|
||||
|
||||
for (let i = 0, n = array1.length; i < n; i++) {
|
||||
for (let i = 0; i < array1.length; i++) {
|
||||
assert.equal(array1[i], array2[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,41 +10,48 @@ namespace ts.server {
|
||||
useCaseSensitiveFileNames: true,
|
||||
write(s): void { lastWrittenToHost = s; },
|
||||
readFile(): string { return void 0; },
|
||||
writeFile(): void {},
|
||||
writeFile: noop,
|
||||
resolvePath(): string { return void 0; },
|
||||
fileExists: () => false,
|
||||
directoryExists: () => false,
|
||||
getDirectories: () => [],
|
||||
createDirectory(): void {},
|
||||
createDirectory: noop,
|
||||
getExecutingFilePath(): string { return void 0; },
|
||||
getCurrentDirectory(): string { return void 0; },
|
||||
getEnvironmentVariable(name: string): string { return ""; },
|
||||
getEnvironmentVariable(): string { return ""; },
|
||||
readDirectory(): string[] { return []; },
|
||||
exit(): void { },
|
||||
setTimeout(callback, ms, ...args) { return 0; },
|
||||
clearTimeout(timeoutId) { },
|
||||
exit: noop,
|
||||
setTimeout() { return 0; },
|
||||
clearTimeout: noop,
|
||||
setImmediate: () => 0,
|
||||
clearImmediate() {}
|
||||
clearImmediate: noop,
|
||||
createHash: s => s
|
||||
};
|
||||
const nullCancellationToken: HostCancellationToken = { isCancellationRequested: () => false };
|
||||
const mockLogger: Logger = {
|
||||
close(): void {},
|
||||
close: noop,
|
||||
hasLevel(): boolean { return false; },
|
||||
loggingEnabled(): boolean { return false; },
|
||||
perftrc(s: string): void {},
|
||||
info(s: string): void {},
|
||||
startGroup(): void {},
|
||||
endGroup(): void {},
|
||||
msg(s: string, type?: string): void {},
|
||||
perftrc: noop,
|
||||
info: noop,
|
||||
startGroup: noop,
|
||||
endGroup: noop,
|
||||
msg: noop,
|
||||
getLogFileName: (): string => undefined
|
||||
};
|
||||
|
||||
class TestSession extends Session {
|
||||
getProjectService() {
|
||||
return this.projectService;
|
||||
}
|
||||
}
|
||||
|
||||
describe("the Session class", () => {
|
||||
let session: Session;
|
||||
let session: TestSession;
|
||||
let lastSent: protocol.Message;
|
||||
|
||||
beforeEach(() => {
|
||||
session = new Session(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
|
||||
session = new TestSession(mockHost, nullCancellationToken, /*useOneInferredProject*/ false, /*typingsInstaller*/ undefined, Utils.byteLength, process.hrtime, mockLogger, /*canUseEvents*/ true);
|
||||
session.send = (msg: protocol.Message) => {
|
||||
lastSent = msg;
|
||||
};
|
||||
@@ -55,7 +62,7 @@ namespace ts.server {
|
||||
const req: protocol.FileRequest = {
|
||||
command: CommandNames.Open,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
type: "request",
|
||||
arguments: {
|
||||
file: undefined
|
||||
}
|
||||
@@ -67,7 +74,7 @@ namespace ts.server {
|
||||
const req: protocol.Request = {
|
||||
command: "foobar",
|
||||
seq: 0,
|
||||
type: "command"
|
||||
type: "request"
|
||||
};
|
||||
|
||||
session.executeCommand(req);
|
||||
@@ -85,7 +92,7 @@ namespace ts.server {
|
||||
const req: protocol.ConfigureRequest = {
|
||||
command: CommandNames.Configure,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
type: "request",
|
||||
arguments: {
|
||||
hostInfo: "unit test",
|
||||
formatOptions: {
|
||||
@@ -106,6 +113,48 @@ namespace ts.server {
|
||||
body: undefined
|
||||
});
|
||||
});
|
||||
it ("should handle literal types in request", () => {
|
||||
const configureRequest: protocol.ConfigureRequest = {
|
||||
command: CommandNames.Configure,
|
||||
seq: 0,
|
||||
type: "request",
|
||||
arguments: {
|
||||
formatOptions: {
|
||||
indentStyle: "Block"
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
session.onMessage(JSON.stringify(configureRequest));
|
||||
|
||||
assert.equal(session.getProjectService().getFormatCodeOptions().indentStyle, IndentStyle.Block);
|
||||
|
||||
const setOptionsRequest: protocol.SetCompilerOptionsForInferredProjectsRequest = {
|
||||
command: CommandNames.CompilerOptionsForInferredProjects,
|
||||
seq: 1,
|
||||
type: "request",
|
||||
arguments: {
|
||||
options: {
|
||||
module: "System",
|
||||
target: "ES5",
|
||||
jsx: "React",
|
||||
newLine: "Lf",
|
||||
moduleResolution: "Node"
|
||||
}
|
||||
}
|
||||
};
|
||||
session.onMessage(JSON.stringify(setOptionsRequest));
|
||||
assert.deepEqual(
|
||||
session.getProjectService().getCompilerOptionsForInferredProjects(),
|
||||
<CompilerOptions>{
|
||||
module: ModuleKind.System,
|
||||
target: ScriptTarget.ES5,
|
||||
jsx: JsxEmit.React,
|
||||
newLine: NewLineKind.LineFeed,
|
||||
moduleResolution: ModuleResolutionKind.NodeJs,
|
||||
allowNonTsExtensions: true // injected by tsserver
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
describe("onMessage", () => {
|
||||
@@ -118,7 +167,7 @@ namespace ts.server {
|
||||
const req: protocol.Request = {
|
||||
command: name,
|
||||
seq: i,
|
||||
type: "command"
|
||||
type: "request"
|
||||
};
|
||||
i++;
|
||||
session.onMessage(JSON.stringify(req));
|
||||
@@ -151,7 +200,7 @@ namespace ts.server {
|
||||
const req: protocol.ConfigureRequest = {
|
||||
command: CommandNames.Configure,
|
||||
seq: 0,
|
||||
type: "command",
|
||||
type: "request",
|
||||
arguments: {
|
||||
hostInfo: "unit test",
|
||||
formatOptions: {
|
||||
@@ -175,7 +224,7 @@ namespace ts.server {
|
||||
|
||||
describe("send", () => {
|
||||
it("is an overrideable handle which sends protocol messages over the wire", () => {
|
||||
const msg = { seq: 0, type: "none" };
|
||||
const msg: server.protocol.Request = { seq: 0, type: "request", command: "" };
|
||||
const strmsg = JSON.stringify(msg);
|
||||
const len = 1 + Utils.byteLength(strmsg, "utf8");
|
||||
const resultMsg = `Content-Length: ${len}\r\n\r\n${strmsg}\n`;
|
||||
@@ -198,12 +247,12 @@ namespace ts.server {
|
||||
responseRequired: true
|
||||
};
|
||||
|
||||
session.addProtocolHandler(command, (req) => result);
|
||||
session.addProtocolHandler(command, () => result);
|
||||
|
||||
expect(session.executeCommand({
|
||||
command,
|
||||
seq: 0,
|
||||
type: "command"
|
||||
type: "request"
|
||||
})).to.deep.equal(result);
|
||||
});
|
||||
it("throws when a duplicate handler is passed", () => {
|
||||
@@ -216,9 +265,9 @@ namespace ts.server {
|
||||
};
|
||||
const command = "newhandle";
|
||||
|
||||
session.addProtocolHandler(command, (req) => resp);
|
||||
session.addProtocolHandler(command, () => resp);
|
||||
|
||||
expect(() => session.addProtocolHandler(command, (req) => resp))
|
||||
expect(() => session.addProtocolHandler(command, () => resp))
|
||||
.to.throw(`Protocol handler already exists for command "${command}"`);
|
||||
});
|
||||
});
|
||||
@@ -304,7 +353,7 @@ namespace ts.server {
|
||||
|
||||
expect(session.executeCommand({
|
||||
seq: 0,
|
||||
type: "command",
|
||||
type: "request",
|
||||
command: session.customHandler
|
||||
})).to.deep.equal({
|
||||
response: undefined,
|
||||
@@ -405,7 +454,7 @@ namespace ts.server {
|
||||
this.seq++;
|
||||
this.server.enqueue({
|
||||
seq: this.seq,
|
||||
type: "command",
|
||||
type: "request",
|
||||
command,
|
||||
arguments: args
|
||||
});
|
||||
|
||||
@@ -0,0 +1,70 @@
|
||||
/// <reference path="../harness.ts" />
|
||||
/// <reference path="../../server/scriptVersionCache.ts"/>
|
||||
/// <reference path="./tsserverProjectSystem.ts" />
|
||||
|
||||
namespace ts.textStorage {
|
||||
describe("Text storage", () => {
|
||||
const f = {
|
||||
path: "/a/app.ts",
|
||||
content: `
|
||||
let x = 1;
|
||||
let y = 2;
|
||||
function bar(a: number) {
|
||||
return a + 1;
|
||||
}`
|
||||
};
|
||||
|
||||
it("text based storage should be have exactly the same as script version cache", () => {
|
||||
|
||||
const host = ts.projectSystem.createServerHost([f]);
|
||||
|
||||
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
const ts2 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
|
||||
ts1.useScriptVersionCache();
|
||||
ts2.useText();
|
||||
|
||||
const lineMap = computeLineStarts(f.content);
|
||||
|
||||
for (let line = 0; line < lineMap.length; line++) {
|
||||
const start = lineMap[line];
|
||||
const end = line === lineMap.length - 1 ? f.path.length : lineMap[line + 1];
|
||||
|
||||
for (let offset = 0; offset < end - start; offset++) {
|
||||
const pos1 = ts1.lineOffsetToPosition(line + 1, offset + 1);
|
||||
const pos2 = ts2.lineOffsetToPosition(line + 1, offset + 1);
|
||||
assert.isTrue(pos1 === pos2, `lineOffsetToPosition ${line + 1}-${offset + 1}: expected ${pos1} to equal ${pos2}`);
|
||||
}
|
||||
|
||||
const {start: start1, length: length1 } = ts1.lineToTextSpan(line);
|
||||
const {start: start2, length: length2 } = ts2.lineToTextSpan(line);
|
||||
assert.isTrue(start1 === start2, `lineToTextSpan ${line}::start:: expected ${start1} to equal ${start2}`);
|
||||
assert.isTrue(length1 === length2, `lineToTextSpan ${line}::length:: expected ${length1} to equal ${length2}`);
|
||||
}
|
||||
|
||||
for (let pos = 0; pos < f.content.length; pos++) {
|
||||
const { line: line1, offset: offset1 } = ts1.positionToLineOffset(pos);
|
||||
const { line: line2, offset: offset2 } = ts2.positionToLineOffset(pos);
|
||||
assert.isTrue(line1 === line2, `positionToLineOffset ${pos}::line:: expected ${line1} to equal ${line2}`);
|
||||
assert.isTrue(offset1 === offset2, `positionToLineOffset ${pos}::offset:: expected ${offset1} to equal ${offset2}`);
|
||||
}
|
||||
});
|
||||
|
||||
it("should switch to script version cache if necessary", () => {
|
||||
const host = ts.projectSystem.createServerHost([f]);
|
||||
const ts1 = new server.TextStorage(host, server.asNormalizedPath(f.path));
|
||||
|
||||
ts1.getSnapshot();
|
||||
assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 1");
|
||||
|
||||
ts1.edit(0, 5, " ");
|
||||
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 1");
|
||||
|
||||
ts1.useText();
|
||||
assert.isTrue(!ts1.hasScriptVersionCache(), "should not have script version cache - 2");
|
||||
|
||||
ts1.getLineInfo(0);
|
||||
assert.isTrue(ts1.hasScriptVersionCache(), "have script version cache - 2");
|
||||
})
|
||||
});
|
||||
}
|
||||
@@ -253,6 +253,10 @@ var x = 0;`, {
|
||||
options: { compilerOptions: { allowUnusedLabels: true }, fileName: "input.js", reportDiagnostics: true }
|
||||
});
|
||||
|
||||
transpilesCorrectly("Supports setting 'alwaysStrict'", "x;", {
|
||||
options: { compilerOptions: { alwaysStrict: true }, fileName: "input.js", reportDiagnostics: true }
|
||||
});
|
||||
|
||||
transpilesCorrectly("Supports setting 'baseUrl'", "x;", {
|
||||
options: { compilerOptions: { baseUrl: "./folder/baseUrl" }, fileName: "input.js", reportDiagnostics: true }
|
||||
});
|
||||
@@ -381,6 +385,10 @@ var x = 0;`, {
|
||||
options: { compilerOptions: { reactNamespace: "react" }, fileName: "input.js", reportDiagnostics: true }
|
||||
});
|
||||
|
||||
transpilesCorrectly("Supports setting 'jsxFactory'", "x;", {
|
||||
options: { compilerOptions: { jsxFactory: "createElement" }, fileName: "input.js", reportDiagnostics: true }
|
||||
});
|
||||
|
||||
transpilesCorrectly("Supports setting 'removeComments'", "x;", {
|
||||
options: { compilerOptions: { removeComments: true }, fileName: "input.js", reportDiagnostics: true }
|
||||
});
|
||||
|
||||
@@ -28,6 +28,14 @@ namespace ts {
|
||||
assert.isTrue(arrayIsEqualTo(parsed.fileNames.sort(), expectedFileList.sort()));
|
||||
}
|
||||
|
||||
function assertParseFileDiagnostics(jsonText: string, configFileName: string, basePath: string, allFileList: string[], expectedDiagnosticCode: number) {
|
||||
const json = JSON.parse(jsonText);
|
||||
const host: ParseConfigHost = new Utils.MockParseConfigHost(basePath, true, allFileList);
|
||||
const parsed = ts.parseJsonConfigFileContent(json, host, basePath, /*existingOptions*/ undefined, configFileName);
|
||||
assert.isTrue(parsed.errors.length >= 0);
|
||||
assert.isTrue(parsed.errors.filter(e => e.code === expectedDiagnosticCode).length > 0, `Expected error code ${expectedDiagnosticCode} to be in ${JSON.stringify(parsed.errors)}`);
|
||||
}
|
||||
|
||||
it("returns empty config for file with only whitespaces", () => {
|
||||
assertParseResult("", { config : {} });
|
||||
assertParseResult(" ", { config : {} });
|
||||
@@ -202,5 +210,64 @@ namespace ts {
|
||||
assert.isTrue(diagnostics.length === 2);
|
||||
assert.equal(JSON.stringify(configJsonObject), JSON.stringify(expectedResult));
|
||||
});
|
||||
|
||||
it("generates errors for empty files list", () => {
|
||||
const content = `{
|
||||
"files": []
|
||||
}`;
|
||||
assertParseFileDiagnostics(content,
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
["/apath/a.ts"],
|
||||
Diagnostics.The_files_list_in_config_file_0_is_empty.code);
|
||||
});
|
||||
|
||||
it("generates errors for directory with no .ts files", () => {
|
||||
const content = `{
|
||||
}`;
|
||||
assertParseFileDiagnostics(content,
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
["/apath/a.js"],
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
});
|
||||
|
||||
it("generates errors for empty directory", () => {
|
||||
const content = `{
|
||||
"compilerOptions": {
|
||||
"allowJs": true
|
||||
}
|
||||
}`;
|
||||
assertParseFileDiagnostics(content,
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
[],
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
});
|
||||
|
||||
it("generates errors for empty include", () => {
|
||||
const content = `{
|
||||
"include": []
|
||||
}`;
|
||||
assertParseFileDiagnostics(content,
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
["/apath/a.ts"],
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
});
|
||||
|
||||
it("generates errors for includes with outDir", () => {
|
||||
const content = `{
|
||||
"compilerOptions": {
|
||||
"outDir": "./"
|
||||
},
|
||||
"include": ["**/*"]
|
||||
}`;
|
||||
assertParseFileDiagnostics(content,
|
||||
"/apath/tsconfig.json",
|
||||
"tests/cases/unittests",
|
||||
["/apath/a.ts"],
|
||||
Diagnostics.No_inputs_were_found_in_config_file_0_Specified_include_paths_were_1_and_exclude_paths_were_2.code);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -8,6 +8,15 @@ namespace ts.projectSystem {
|
||||
interface InstallerParams {
|
||||
globalTypingsCacheLocation?: string;
|
||||
throttleLimit?: number;
|
||||
typesRegistry?: Map<void>;
|
||||
}
|
||||
|
||||
function createTypesRegistry(...list: string[]): Map<void> {
|
||||
const map = createMap<void>();
|
||||
for (const l of list) {
|
||||
map[l] = undefined;
|
||||
}
|
||||
return map;
|
||||
}
|
||||
|
||||
class Installer extends TestTypingsInstaller {
|
||||
@@ -16,36 +25,26 @@ namespace ts.projectSystem {
|
||||
(p && p.globalTypingsCacheLocation) || "/a/data",
|
||||
(p && p.throttleLimit) || 5,
|
||||
host,
|
||||
(p && p.typesRegistry),
|
||||
log);
|
||||
}
|
||||
|
||||
installAll(expectedView: typeof TI.NpmViewRequest[], expectedInstall: typeof TI.NpmInstallRequest[]) {
|
||||
this.checkPendingCommands(expectedView);
|
||||
this.executePendingCommands();
|
||||
this.checkPendingCommands(expectedInstall);
|
||||
installAll(expectedCount: number) {
|
||||
this.checkPendingCommands(expectedCount);
|
||||
this.executePendingCommands();
|
||||
}
|
||||
}
|
||||
|
||||
describe("typingsInstaller", () => {
|
||||
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[], typingFiles: FileOrFolder[], requestKind: TI.RequestKind, cb: TI.RequestCompletedAction): void {
|
||||
switch (requestKind) {
|
||||
case TI.NpmInstallRequest:
|
||||
self.addPostExecAction(requestKind, installedTypings, (err, stdout, stderr) => {
|
||||
for (const file of typingFiles) {
|
||||
host.createFileOrFolder(file, /*createParentDirectory*/ true);
|
||||
}
|
||||
cb(err, stdout, stderr);
|
||||
});
|
||||
break;
|
||||
case TI.NpmViewRequest:
|
||||
self.addPostExecAction(requestKind, installedTypings, cb);
|
||||
break;
|
||||
default:
|
||||
assert.isTrue(false, `unexpected request kind ${requestKind}`);
|
||||
break;
|
||||
function executeCommand(self: Installer, host: TestServerHost, installedTypings: string[] | string, typingFiles: FileOrFolder[], cb: TI.RequestCompletedAction): void {
|
||||
self.addPostExecAction(installedTypings, success => {
|
||||
for (const file of typingFiles) {
|
||||
host.createFileOrFolder(file, /*createParentDirectory*/ true);
|
||||
}
|
||||
}
|
||||
cb(success);
|
||||
});
|
||||
}
|
||||
|
||||
describe("typingsInstaller", () => {
|
||||
it("configured projects (typings installed) 1", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.js",
|
||||
@@ -57,8 +56,8 @@ namespace ts.projectSystem {
|
||||
compilerOptions: {
|
||||
allowJs: true
|
||||
},
|
||||
typingOptions: {
|
||||
enableAutoDiscovery: true
|
||||
typeAcquisition: {
|
||||
enable: true
|
||||
}
|
||||
})
|
||||
};
|
||||
@@ -79,12 +78,12 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([file1, tsconfig, packageJson]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host);
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -95,7 +94,7 @@ namespace ts.projectSystem {
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [file1.path]);
|
||||
|
||||
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
@@ -123,12 +122,12 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([file1, packageJson]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host);
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -139,13 +138,13 @@ namespace ts.projectSystem {
|
||||
const p = projectService.inferredProjects[0];
|
||||
checkProjectActualFiles(p, [file1.path]);
|
||||
|
||||
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, jquery.path]);
|
||||
});
|
||||
|
||||
it("external project - no typing options, no .d.ts/js files", () => {
|
||||
it("external project - no type acquisition, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
@@ -167,13 +166,13 @@ namespace ts.projectSystem {
|
||||
options: {},
|
||||
rootFiles: [toExternalFile(file1.path)]
|
||||
});
|
||||
installer.checkPendingCommands([]);
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
// by default auto discovery will kick in if project contain only .js/.d.ts files
|
||||
// in this case project contain only ts files - no auto discovery
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
it("external project - no autoDiscovery in typing options, no .d.ts/js files", () => {
|
||||
it("external project - no auto in typing acquisition, no .d.ts/js files", () => {
|
||||
const file1 = {
|
||||
path: "/a/b/app.ts",
|
||||
content: ""
|
||||
@@ -181,7 +180,7 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([file1]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host);
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest() {
|
||||
assert(false, "auto discovery should not be enabled");
|
||||
@@ -194,11 +193,11 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: [toExternalFile(file1.path)],
|
||||
typingOptions: { include: ["jquery"] }
|
||||
typeAcquisition: { include: ["jquery"] }
|
||||
});
|
||||
installer.checkPendingCommands([]);
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
// by default auto discovery will kick in if project contain only .js/.d.ts files
|
||||
// in this case project contain only ts files - no auto discovery even if typing options is set
|
||||
// in this case project contain only ts files - no auto discovery even if type acquisition is set
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
@@ -215,16 +214,16 @@ namespace ts.projectSystem {
|
||||
let enqueueIsCalled = false;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host);
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueIsCalled = true;
|
||||
super.enqueueInstallTypingsRequest(project, typingOptions);
|
||||
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/node"];
|
||||
const typingFiles = [jquery];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -234,17 +233,17 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: {},
|
||||
rootFiles: [toExternalFile(file1.path)],
|
||||
typingOptions: { enableAutoDiscovery: true, include: ["node"] }
|
||||
typeAcquisition: { enable: true, include: ["jquery"] }
|
||||
});
|
||||
|
||||
assert.isTrue(enqueueIsCalled, "expected enqueueIsCalled to be true");
|
||||
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
// autoDiscovery is set in typing options - use it even if project contains only .ts files
|
||||
// auto is set in type acquisition - use it even if project contains only .ts files
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
});
|
||||
|
||||
it("external project - no typing options, with only js, jsx, d.ts files", () => {
|
||||
it("external project - no type acquisition, with only js, jsx, d.ts files", () => {
|
||||
// Tests:
|
||||
// 1. react typings are installed for .jsx
|
||||
// 2. loose files names are matched against safe list for typings if
|
||||
@@ -273,12 +272,12 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([file1, file2, file3]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host);
|
||||
super(host, { typesRegistry: createTypesRegistry("lodash", "react") });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/lodash", "@types/react"];
|
||||
const typingFiles = [lodash, react];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -288,20 +287,20 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path), toExternalFile(file3.path)],
|
||||
typingOptions: {}
|
||||
typeAcquisition: {}
|
||||
});
|
||||
|
||||
const p = projectService.externalProjects[0];
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, file2.path, file3.path]);
|
||||
|
||||
installer.installAll([TI.NpmViewRequest, TI.NpmViewRequest], [TI.NpmInstallRequest], );
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, lodash.path, react.path]);
|
||||
});
|
||||
|
||||
it("external project - no typing options, with js & ts files", () => {
|
||||
it("external project - no type acquisition, with js & ts files", () => {
|
||||
// Tests:
|
||||
// 1. No typings are included for JS projects when the project contains ts files
|
||||
const file1 = {
|
||||
@@ -317,16 +316,16 @@ namespace ts.projectSystem {
|
||||
let enqueueIsCalled = false;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host);
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
enqueueInstallTypingsRequest(project: server.Project, typingOptions: TypingOptions) {
|
||||
enqueueInstallTypingsRequest(project: server.Project, typeAcquisition: TypeAcquisition, unresolvedImports: server.SortedReadonlyArray<string>) {
|
||||
enqueueIsCalled = true;
|
||||
super.enqueueInstallTypingsRequest(project, typingOptions);
|
||||
super.enqueueInstallTypingsRequest(project, typeAcquisition, unresolvedImports);
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings: string[] = [];
|
||||
const typingFiles: FileOrFolder[] = [];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -336,24 +335,24 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path)],
|
||||
typingOptions: {}
|
||||
typeAcquisition: {}
|
||||
});
|
||||
|
||||
const p = projectService.externalProjects[0];
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, file2.path]);
|
||||
|
||||
installer.checkPendingCommands([]);
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, file2.path]);
|
||||
});
|
||||
|
||||
it("external project - with typing options, with only js, d.ts files", () => {
|
||||
it("external project - with type acquisition, with only js, d.ts files", () => {
|
||||
// Tests:
|
||||
// 1. Safelist matching, typing options includes/excludes and package.json typings are all acquired
|
||||
// 2. Types for safelist matches are not included when they also appear in the typing option exclude list
|
||||
// 3. Multiple includes and excludes are respected in typing options
|
||||
// 1. Safelist matching, type acquisition includes/excludes and package.json typings are all acquired
|
||||
// 2. Types for safelist matches are not included when they also appear in the type acquisition exclude list
|
||||
// 3. Multiple includes and excludes are respected in type acquisition
|
||||
const file1 = {
|
||||
path: "/a/b/lodash.js",
|
||||
content: ""
|
||||
@@ -396,12 +395,12 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([file1, file2, file3, packageJson]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host);
|
||||
super(host, { typesRegistry: createTypesRegistry("jquery", "commander", "moment", "express") });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment"];
|
||||
const typingFiles = [commander, express, jquery, moment];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -411,17 +410,14 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(file1.path), toExternalFile(file2.path), toExternalFile(file3.path)],
|
||||
typingOptions: { include: ["jquery", "moment"], exclude: ["lodash"] }
|
||||
typeAcquisition: { include: ["jquery", "moment"], exclude: ["lodash"] }
|
||||
});
|
||||
|
||||
const p = projectService.externalProjects[0];
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, file2.path, file3.path]);
|
||||
|
||||
installer.installAll(
|
||||
[TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest],
|
||||
[TI.NpmInstallRequest]
|
||||
);
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [file1.path, file2.path, file3.path, commander.path, express.path, jquery.path, moment.path]);
|
||||
@@ -475,11 +471,11 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([lodashJs, commanderJs, file3, packageJson]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { throttleLimit: 3 });
|
||||
super(host, { throttleLimit: 3, typesRegistry: createTypesRegistry("commander", "express", "jquery", "moment", "lodash") });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
const installedTypings = ["@types/commander", "@types/express", "@types/jquery", "@types/moment", "@types/lodash"];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -489,24 +485,13 @@ namespace ts.projectSystem {
|
||||
projectFileName,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)],
|
||||
typingOptions: { include: ["jquery", "moment"] }
|
||||
typeAcquisition: { include: ["jquery", "moment"] }
|
||||
});
|
||||
|
||||
const p = projectService.externalProjects[0];
|
||||
projectService.checkNumberOfProjects({ externalProjects: 1 });
|
||||
checkProjectActualFiles(p, [lodashJs.path, commanderJs.path, file3.path]);
|
||||
// expected 3 view requests in the queue
|
||||
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
|
||||
assert.equal(installer.pendingRunRequests.length, 2, "expected 2 pending requests");
|
||||
|
||||
// push view requests
|
||||
installer.executePendingCommands();
|
||||
// expected 2 remaining view requests in the queue
|
||||
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest]);
|
||||
// push view requests
|
||||
installer.executePendingCommands();
|
||||
// expected one install request
|
||||
installer.checkPendingCommands([TI.NpmInstallRequest]);
|
||||
installer.checkPendingCommands(/*expectedCount*/ 1);
|
||||
installer.executePendingCommands();
|
||||
// expected all typings file to exist
|
||||
for (const f of typingFiles) {
|
||||
@@ -565,22 +550,17 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([lodashJs, commanderJs, file3]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { throttleLimit: 3 });
|
||||
super(host, { throttleLimit: 1, typesRegistry: createTypesRegistry("commander", "jquery", "lodash", "cordova", "gulp", "grunt") });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
if (requestKind === TI.NpmInstallRequest) {
|
||||
let typingFiles: (FileOrFolder & { typings: string}) [] = [];
|
||||
if (command.indexOf("commander") >= 0) {
|
||||
typingFiles = [commander, jquery, lodash, cordova];
|
||||
}
|
||||
else {
|
||||
typingFiles = [grunt, gulp];
|
||||
}
|
||||
executeCommand(this, host, typingFiles.map(f => f.typings), typingFiles, requestKind, cb);
|
||||
installWorker(_requestId: number, args: string[], _cwd: string, cb: TI.RequestCompletedAction): void {
|
||||
let typingFiles: (FileOrFolder & { typings: string })[] = [];
|
||||
if (args.indexOf("@types/commander") >= 0) {
|
||||
typingFiles = [commander, jquery, lodash, cordova];
|
||||
}
|
||||
else {
|
||||
executeCommand(this, host, [], [], requestKind, cb);
|
||||
typingFiles = [grunt, gulp];
|
||||
}
|
||||
executeCommand(this, host, typingFiles.map(f => f.typings), typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -591,11 +571,11 @@ namespace ts.projectSystem {
|
||||
projectFileName: projectFileName1,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(lodashJs.path), toExternalFile(commanderJs.path), toExternalFile(file3.path)],
|
||||
typingOptions: { include: ["jquery", "cordova" ] }
|
||||
typeAcquisition: { include: ["jquery", "cordova"] }
|
||||
});
|
||||
|
||||
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
|
||||
assert.equal(installer.pendingRunRequests.length, 1, "expect one throttled request");
|
||||
installer.checkPendingCommands(/*expectedCount*/ 1);
|
||||
assert.equal(installer.pendingRunRequests.length, 0, "expect no throttled requests");
|
||||
|
||||
// Create project #2 with 2 typings
|
||||
const projectFileName2 = "/a/app/test2.csproj";
|
||||
@@ -603,9 +583,9 @@ namespace ts.projectSystem {
|
||||
projectFileName: projectFileName2,
|
||||
options: { allowJS: true, moduleResolution: ModuleResolutionKind.NodeJs },
|
||||
rootFiles: [toExternalFile(file3.path)],
|
||||
typingOptions: { include: ["grunt", "gulp"] }
|
||||
typeAcquisition: { include: ["grunt", "gulp"] }
|
||||
});
|
||||
assert.equal(installer.pendingRunRequests.length, 3, "expect three throttled request");
|
||||
assert.equal(installer.pendingRunRequests.length, 1, "expect one throttled request");
|
||||
|
||||
const p1 = projectService.externalProjects[0];
|
||||
const p2 = projectService.externalProjects[1];
|
||||
@@ -613,20 +593,16 @@ namespace ts.projectSystem {
|
||||
checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path]);
|
||||
checkProjectActualFiles(p2, [file3.path]);
|
||||
|
||||
|
||||
installer.executePendingCommands();
|
||||
// expected one view request from the first project and two - from the second one
|
||||
installer.checkPendingCommands([TI.NpmViewRequest, TI.NpmViewRequest, TI.NpmViewRequest]);
|
||||
|
||||
// expected one install request from the second project
|
||||
installer.checkPendingCommands(/*expectedCount*/ 1);
|
||||
assert.equal(installer.pendingRunRequests.length, 0, "expected no throttled requests");
|
||||
|
||||
installer.executePendingCommands();
|
||||
|
||||
// should be two install requests from both projects
|
||||
installer.checkPendingCommands([TI.NpmInstallRequest, TI.NpmInstallRequest]);
|
||||
installer.executePendingCommands();
|
||||
|
||||
checkProjectActualFiles(p1, [lodashJs.path, commanderJs.path, file3.path, commander.path, jquery.path, lodash.path, cordova.path]);
|
||||
checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path ]);
|
||||
checkProjectActualFiles(p2, [file3.path, grunt.path, gulp.path]);
|
||||
});
|
||||
|
||||
it("configured projects discover from node_modules", () => {
|
||||
@@ -653,12 +629,12 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([app, jsconfig, jquery, jqueryPackage]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp" });
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jqueryDTS];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -669,7 +645,7 @@ namespace ts.projectSystem {
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [app.path]);
|
||||
|
||||
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
|
||||
@@ -687,10 +663,10 @@ namespace ts.projectSystem {
|
||||
const bowerJson = {
|
||||
path: "/bower.json",
|
||||
content: JSON.stringify({
|
||||
"dependencies": {
|
||||
"jquery": "^3.1.0"
|
||||
}
|
||||
})
|
||||
"dependencies": {
|
||||
"jquery": "^3.1.0"
|
||||
}
|
||||
})
|
||||
};
|
||||
const jqueryDTS = {
|
||||
path: "/tmp/node_modules/@types/jquery/index.d.ts",
|
||||
@@ -699,12 +675,12 @@ namespace ts.projectSystem {
|
||||
const host = createServerHost([app, jsconfig, bowerJson]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp" });
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("jquery") });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/jquery"];
|
||||
const typingFiles = [jqueryDTS];
|
||||
executeCommand(this, host, installedTypings, typingFiles, requestKind, cb);
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -715,31 +691,197 @@ namespace ts.projectSystem {
|
||||
const p = projectService.configuredProjects[0];
|
||||
checkProjectActualFiles(p, [app.path]);
|
||||
|
||||
installer.installAll([TI.NpmViewRequest], [TI.NpmInstallRequest]);
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
checkNumberOfProjects(projectService, { configuredProjects: 1 });
|
||||
checkProjectActualFiles(p, [app.path, jqueryDTS.path]);
|
||||
});
|
||||
|
||||
it("Malformed package.json should be watched", () => {
|
||||
const f = {
|
||||
path: "/a/b/app.js",
|
||||
content: "var x = 1"
|
||||
};
|
||||
const brokenPackageJson = {
|
||||
path: "/a/b/package.json",
|
||||
content: `{ "dependencies": { "co } }`
|
||||
};
|
||||
const fixedPackageJson = {
|
||||
path: brokenPackageJson.path,
|
||||
content: `{ "dependencies": { "commander": "0.0.2" } }`
|
||||
};
|
||||
const cachePath = "/a/cache/";
|
||||
const commander = {
|
||||
path: cachePath + "node_modules/@types/commander/index.d.ts",
|
||||
content: "export let x: number"
|
||||
};
|
||||
const host = createServerHost([f, brokenPackageJson]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/commander"];
|
||||
const typingFiles = [commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
const service = createProjectService(host, { typingsInstaller: installer });
|
||||
service.openClientFile(f.path);
|
||||
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
|
||||
host.reloadFS([f, fixedPackageJson]);
|
||||
host.triggerFileWatcherCallback(fixedPackageJson.path, /*removed*/ false);
|
||||
// expected install request
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
service.checkNumberOfProjects({ inferredProjects: 1 });
|
||||
checkProjectActualFiles(service.inferredProjects[0], [f.path, commander.path]);
|
||||
});
|
||||
|
||||
it("should install typings for unresolved imports", () => {
|
||||
const file = {
|
||||
path: "/a/b/app.js",
|
||||
content: `
|
||||
import * as fs from "fs";
|
||||
import * as commander from "commander";`
|
||||
};
|
||||
const cachePath = "/a/cache";
|
||||
const node = {
|
||||
path: cachePath + "/node_modules/@types/node/index.d.ts",
|
||||
content: "export let x: number"
|
||||
};
|
||||
const commander = {
|
||||
path: cachePath + "/node_modules/@types/commander/index.d.ts",
|
||||
content: "export let y: string"
|
||||
};
|
||||
const host = createServerHost([file]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("node", "commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/node", "@types/commander"];
|
||||
const typingFiles = [node, commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
})();
|
||||
const service = createProjectService(host, { typingsInstaller: installer });
|
||||
service.openClientFile(file.path);
|
||||
|
||||
service.checkNumberOfProjects({ inferredProjects: 1 });
|
||||
checkProjectActualFiles(service.inferredProjects[0], [file.path]);
|
||||
|
||||
installer.installAll(/*expectedCount*/1);
|
||||
|
||||
assert.isTrue(host.fileExists(node.path), "typings for 'node' should be created");
|
||||
assert.isTrue(host.fileExists(commander.path), "typings for 'commander' should be created");
|
||||
|
||||
checkProjectActualFiles(service.inferredProjects[0], [file.path, node.path, commander.path]);
|
||||
});
|
||||
|
||||
it("should pick typing names from non-relative unresolved imports", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.js",
|
||||
content: `
|
||||
import * as a from "foo/a/a";
|
||||
import * as b from "foo/a/b";
|
||||
import * as c from "foo/a/c";
|
||||
import * as d from "@bar/router/";
|
||||
import * as e from "@bar/common/shared";
|
||||
import * as e from "@bar/common/apps";
|
||||
import * as f from "./lib"
|
||||
`
|
||||
};
|
||||
|
||||
const host = createServerHost([f1]);
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp", typesRegistry: createTypesRegistry("foo") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
executeCommand(this, host, ["foo"], [], cb);
|
||||
}
|
||||
})();
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
projectService.checkNumberOfProjects({ inferredProjects: 1 });
|
||||
|
||||
const proj = projectService.inferredProjects[0];
|
||||
proj.updateGraph();
|
||||
|
||||
assert.deepEqual(
|
||||
proj.getCachedUnresolvedImportsPerFile_TestOnly().get(<Path>f1.path),
|
||||
["foo", "foo", "foo", "@bar/router", "@bar/common", "@bar/common"]
|
||||
);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
});
|
||||
|
||||
it("cached unresolved typings are not recomputed if program structure did not change", () => {
|
||||
const host = createServerHost([]);
|
||||
const session = createSession(host);
|
||||
const f = {
|
||||
path: "/a/app.js",
|
||||
content: `
|
||||
import * as fs from "fs";
|
||||
import * as cmd from "commander
|
||||
`
|
||||
};
|
||||
session.executeCommand(<server.protocol.OpenRequest>{
|
||||
seq: 1,
|
||||
type: "request",
|
||||
command: "open",
|
||||
arguments: {
|
||||
file: f.path,
|
||||
fileContent: f.content
|
||||
}
|
||||
});
|
||||
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>{
|
||||
seq: 2,
|
||||
type: "request",
|
||||
command: "change",
|
||||
arguments: {
|
||||
file: f.path,
|
||||
insertString: "\nlet x = 1;",
|
||||
line: 2,
|
||||
offset: 0,
|
||||
endLine: 2,
|
||||
endOffset: 0
|
||||
}
|
||||
});
|
||||
host.checkTimeoutQueueLength(1);
|
||||
host.runQueuedTimeoutCallbacks();
|
||||
const version2 = proj.getCachedUnresolvedImportsPerFile_TestOnly().getVersion();
|
||||
assert.equal(version1, version2, "set of unresolved imports should not change");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Validate package name:", () => {
|
||||
it ("name cannot be too long", () => {
|
||||
it("name cannot be too long", () => {
|
||||
let packageName = "a";
|
||||
for (let i = 0; i < 8; i++) {
|
||||
packageName += packageName;
|
||||
}
|
||||
assert.equal(TI.validatePackageName(packageName), TI.PackageNameValidationResult.NameTooLong);
|
||||
});
|
||||
it ("name cannot start with dot", () => {
|
||||
it("name cannot start with dot", () => {
|
||||
assert.equal(TI.validatePackageName(".foo"), TI.PackageNameValidationResult.NameStartsWithDot);
|
||||
});
|
||||
it ("name cannot start with underscore", () => {
|
||||
it("name cannot start with underscore", () => {
|
||||
assert.equal(TI.validatePackageName("_foo"), TI.PackageNameValidationResult.NameStartsWithUnderscore);
|
||||
});
|
||||
it ("scoped packages not supported", () => {
|
||||
it("scoped packages not supported", () => {
|
||||
assert.equal(TI.validatePackageName("@scope/bar"), TI.PackageNameValidationResult.ScopedPackagesNotSupported);
|
||||
});
|
||||
it ("non URI safe characters are not supported", () => {
|
||||
it("non URI safe characters are not supported", () => {
|
||||
assert.equal(TI.validatePackageName(" scope "), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters);
|
||||
assert.equal(TI.validatePackageName("; say ‘Hello from TypeScript!’ #"), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters);
|
||||
assert.equal(TI.validatePackageName("a/b/c"), TI.PackageNameValidationResult.NameContainsNonURISafeCharacters);
|
||||
@@ -747,7 +889,7 @@ namespace ts.projectSystem {
|
||||
});
|
||||
|
||||
describe("Invalid package names", () => {
|
||||
it ("should not be installed", () => {
|
||||
it("should not be installed", () => {
|
||||
const f1 = {
|
||||
path: "/a/b/app.js",
|
||||
content: "let x = 1"
|
||||
@@ -766,15 +908,193 @@ namespace ts.projectSystem {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: "/tmp" }, { isEnabled: () => true, writeLine: msg => messages.push(msg) });
|
||||
}
|
||||
runCommand(requestKind: TI.RequestKind, requestId: number, command: string, cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, _cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
assert(false, "runCommand should not be invoked");
|
||||
}
|
||||
})();
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.checkPendingCommands([]);
|
||||
installer.checkPendingCommands(/*expectedCount*/ 0);
|
||||
assert.isTrue(messages.indexOf("Package name '; say ‘Hello from TypeScript!’ #' contains non URI safe characters") > 0, "should find package with invalid name");
|
||||
});
|
||||
});
|
||||
|
||||
describe("discover typings", () => {
|
||||
it("should return node for core modules", () => {
|
||||
const f = {
|
||||
path: "/a/b/app.js",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([f]);
|
||||
const cache = createMap<string>();
|
||||
for (const name of JsTyping.nodeCoreModuleList) {
|
||||
const result = JsTyping.discoverTypings(host, [f.path], getDirectoryPath(<Path>f.path), /*safeListPath*/ undefined, cache, { enable: true }, [name, "somename"]);
|
||||
assert.deepEqual(result.newTypingNames.sort(), ["node", "somename"]);
|
||||
}
|
||||
});
|
||||
|
||||
it("should use cached locaitons", () => {
|
||||
const f = {
|
||||
path: "/a/b/app.js",
|
||||
content: ""
|
||||
};
|
||||
const node = {
|
||||
path: "/a/b/node.d.ts",
|
||||
content: ""
|
||||
};
|
||||
const host = createServerHost([f, node]);
|
||||
const cache = createMap<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"]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("telemetry events", () => {
|
||||
it ("should be received", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.js",
|
||||
content: ""
|
||||
};
|
||||
const package = {
|
||||
path: "/a/package.json",
|
||||
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
|
||||
};
|
||||
const cachePath = "/a/cache/";
|
||||
const commander = {
|
||||
path: cachePath + "node_modules/@types/commander/index.d.ts",
|
||||
content: "export let x: number"
|
||||
};
|
||||
const host = createServerHost([f1, package]);
|
||||
let seenTelemetryEvent = false;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/commander"];
|
||||
const typingFiles = [commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
return;
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
assert.deepEqual(response.packagesToInstall, ["@types/commander"]);
|
||||
seenTelemetryEvent = true;
|
||||
return;
|
||||
}
|
||||
super.sendResponse(response);
|
||||
}
|
||||
})();
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
assert.isTrue(seenTelemetryEvent);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
|
||||
});
|
||||
});
|
||||
|
||||
describe("progress notifications", () => {
|
||||
it ("should be sent for success", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.js",
|
||||
content: ""
|
||||
};
|
||||
const package = {
|
||||
path: "/a/package.json",
|
||||
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
|
||||
};
|
||||
const cachePath = "/a/cache/";
|
||||
const commander = {
|
||||
path: cachePath + "node_modules/@types/commander/index.d.ts",
|
||||
content: "export let x: number"
|
||||
};
|
||||
const host = createServerHost([f1, package]);
|
||||
let beginEvent: server.BeginInstallTypes;
|
||||
let endEvent: server.EndInstallTypes;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
const installedTypings = ["@types/commander"];
|
||||
const typingFiles = [commander];
|
||||
executeCommand(this, host, installedTypings, typingFiles, cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
beginEvent = response;
|
||||
return;
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
endEvent = response;
|
||||
return;
|
||||
}
|
||||
super.sendResponse(response);
|
||||
}
|
||||
})();
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
assert.isTrue(!!beginEvent);
|
||||
assert.isTrue(!!endEvent);
|
||||
assert.isTrue(beginEvent.eventId === endEvent.eventId);
|
||||
assert.isTrue(endEvent.installSuccess);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path, commander.path]);
|
||||
});
|
||||
|
||||
it ("should be sent for error", () => {
|
||||
const f1 = {
|
||||
path: "/a/app.js",
|
||||
content: ""
|
||||
};
|
||||
const package = {
|
||||
path: "/a/package.json",
|
||||
content: JSON.stringify({ dependencies: { "commander": "1.0.0" } })
|
||||
};
|
||||
const cachePath = "/a/cache/";
|
||||
const host = createServerHost([f1, package]);
|
||||
let beginEvent: server.BeginInstallTypes;
|
||||
let endEvent: server.EndInstallTypes;
|
||||
const installer = new (class extends Installer {
|
||||
constructor() {
|
||||
super(host, { globalTypingsCacheLocation: cachePath, typesRegistry: createTypesRegistry("commander") });
|
||||
}
|
||||
installWorker(_requestId: number, _args: string[], _cwd: string, cb: server.typingsInstaller.RequestCompletedAction) {
|
||||
executeCommand(this, host, "", [], cb);
|
||||
}
|
||||
sendResponse(response: server.SetTypings | server.InvalidateCachedTypings | server.BeginInstallTypes | server.EndInstallTypes) {
|
||||
if (response.kind === server.EventBeginInstallTypes) {
|
||||
beginEvent = response;
|
||||
return;
|
||||
}
|
||||
if (response.kind === server.EventEndInstallTypes) {
|
||||
endEvent = response;
|
||||
return;
|
||||
}
|
||||
super.sendResponse(response);
|
||||
}
|
||||
})();
|
||||
const projectService = createProjectService(host, { typingsInstaller: installer });
|
||||
projectService.openClientFile(f1.path);
|
||||
|
||||
installer.installAll(/*expectedCount*/ 1);
|
||||
|
||||
assert.isTrue(!!beginEvent);
|
||||
assert.isTrue(!!endEvent);
|
||||
assert.isTrue(beginEvent.eventId === endEvent.eventId);
|
||||
assert.isFalse(endEvent.installSuccess);
|
||||
checkNumberOfProjects(projectService, { inferredProjects: 1 });
|
||||
checkProjectActualFiles(projectService.inferredProjects[0], [f1.path]);
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -307,7 +307,7 @@ and grew 1cm per day`;
|
||||
|
||||
it("Start pos from line", () => {
|
||||
for (let i = 0; i < iterationCount; i++) {
|
||||
for (let j = 0, llen = lines.length; j < llen; j++) {
|
||||
for (let j = 0; j < lines.length; j++) {
|
||||
const lineInfo = lineIndex.lineNumberToInfo(j + 1);
|
||||
const lineIndexOffset = lineInfo.offset;
|
||||
const lineMapOffset = lineMap[j];
|
||||
|
||||
Reference in New Issue
Block a user