2020-01-21 10:01:55 -05:00
|
|
|
// Copyright 2018-2020 the Deno authors. All rights reserved. MIT license.
|
2020-01-08 09:17:44 -05:00
|
|
|
|
|
|
|
// These utilities are used by compiler.ts to format TypeScript diagnostics
|
|
|
|
// into Deno Diagnostics.
|
|
|
|
|
|
|
|
import {
|
|
|
|
Diagnostic,
|
|
|
|
DiagnosticCategory,
|
|
|
|
DiagnosticMessageChain,
|
|
|
|
DiagnosticItem
|
|
|
|
} from "./diagnostics.ts";
|
|
|
|
|
|
|
|
interface SourceInformation {
|
|
|
|
sourceLine: string;
|
|
|
|
lineNumber: number;
|
|
|
|
scriptResourceName: string;
|
|
|
|
startColumn: number;
|
|
|
|
endColumn: number;
|
|
|
|
}
|
|
|
|
|
|
|
|
function fromDiagnosticCategory(
|
|
|
|
category: ts.DiagnosticCategory
|
|
|
|
): DiagnosticCategory {
|
|
|
|
switch (category) {
|
|
|
|
case ts.DiagnosticCategory.Error:
|
|
|
|
return DiagnosticCategory.Error;
|
|
|
|
case ts.DiagnosticCategory.Message:
|
|
|
|
return DiagnosticCategory.Info;
|
|
|
|
case ts.DiagnosticCategory.Suggestion:
|
|
|
|
return DiagnosticCategory.Suggestion;
|
|
|
|
case ts.DiagnosticCategory.Warning:
|
|
|
|
return DiagnosticCategory.Warning;
|
|
|
|
default:
|
|
|
|
throw new Error(
|
|
|
|
`Unexpected DiagnosticCategory: "${category}"/"${ts.DiagnosticCategory[category]}"`
|
|
|
|
);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
function getSourceInformation(
|
|
|
|
sourceFile: ts.SourceFile,
|
|
|
|
start: number,
|
|
|
|
length: number
|
|
|
|
): SourceInformation {
|
|
|
|
const scriptResourceName = sourceFile.fileName;
|
|
|
|
const {
|
|
|
|
line: lineNumber,
|
|
|
|
character: startColumn
|
|
|
|
} = sourceFile.getLineAndCharacterOfPosition(start);
|
|
|
|
const endPosition = sourceFile.getLineAndCharacterOfPosition(start + length);
|
|
|
|
const endColumn =
|
|
|
|
lineNumber === endPosition.line ? endPosition.character : startColumn;
|
|
|
|
const lastLineInFile = sourceFile.getLineAndCharacterOfPosition(
|
|
|
|
sourceFile.text.length
|
|
|
|
).line;
|
|
|
|
const lineStart = sourceFile.getPositionOfLineAndCharacter(lineNumber, 0);
|
|
|
|
const lineEnd =
|
|
|
|
lineNumber < lastLineInFile
|
|
|
|
? sourceFile.getPositionOfLineAndCharacter(lineNumber + 1, 0)
|
|
|
|
: sourceFile.text.length;
|
|
|
|
const sourceLine = sourceFile.text
|
|
|
|
.slice(lineStart, lineEnd)
|
|
|
|
.replace(/\s+$/g, "")
|
|
|
|
.replace("\t", " ");
|
|
|
|
return {
|
|
|
|
sourceLine,
|
|
|
|
lineNumber,
|
|
|
|
scriptResourceName,
|
|
|
|
startColumn,
|
|
|
|
endColumn
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Converts a TypeScript diagnostic message chain to a Deno one. */
|
|
|
|
function fromDiagnosticMessageChain(
|
|
|
|
messageChain: ts.DiagnosticMessageChain[] | undefined
|
|
|
|
): DiagnosticMessageChain[] | undefined {
|
|
|
|
if (!messageChain) {
|
|
|
|
return undefined;
|
|
|
|
}
|
|
|
|
|
|
|
|
return messageChain.map(({ messageText: message, code, category, next }) => {
|
|
|
|
return {
|
|
|
|
message,
|
|
|
|
code,
|
|
|
|
category: fromDiagnosticCategory(category),
|
|
|
|
next: fromDiagnosticMessageChain(next)
|
|
|
|
};
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Parse out information from a TypeScript diagnostic structure. */
|
|
|
|
function parseDiagnostic(
|
|
|
|
item: ts.Diagnostic | ts.DiagnosticRelatedInformation
|
|
|
|
): DiagnosticItem {
|
|
|
|
const {
|
|
|
|
messageText,
|
|
|
|
category: sourceCategory,
|
|
|
|
code,
|
|
|
|
file,
|
|
|
|
start: startPosition,
|
|
|
|
length
|
|
|
|
} = item;
|
|
|
|
const sourceInfo =
|
|
|
|
file && startPosition && length
|
|
|
|
? getSourceInformation(file, startPosition, length)
|
|
|
|
: undefined;
|
|
|
|
const endPosition =
|
|
|
|
startPosition && length ? startPosition + length : undefined;
|
|
|
|
const category = fromDiagnosticCategory(sourceCategory);
|
|
|
|
|
|
|
|
let message: string;
|
|
|
|
let messageChain: DiagnosticMessageChain | undefined;
|
|
|
|
if (typeof messageText === "string") {
|
|
|
|
message = messageText;
|
|
|
|
} else {
|
|
|
|
message = messageText.messageText;
|
|
|
|
messageChain = fromDiagnosticMessageChain([messageText])![0];
|
|
|
|
}
|
|
|
|
|
|
|
|
const base = {
|
|
|
|
message,
|
|
|
|
messageChain,
|
|
|
|
code,
|
|
|
|
category,
|
|
|
|
startPosition,
|
|
|
|
endPosition
|
|
|
|
};
|
|
|
|
|
|
|
|
return sourceInfo ? { ...base, ...sourceInfo } : base;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Convert a diagnostic related information array into a Deno diagnostic
|
|
|
|
* array. */
|
|
|
|
function parseRelatedInformation(
|
|
|
|
relatedInformation: readonly ts.DiagnosticRelatedInformation[]
|
|
|
|
): DiagnosticItem[] {
|
|
|
|
const result: DiagnosticItem[] = [];
|
|
|
|
for (const item of relatedInformation) {
|
|
|
|
result.push(parseDiagnostic(item));
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** Convert TypeScript diagnostics to Deno diagnostics. */
|
|
|
|
export function fromTypeScriptDiagnostic(
|
|
|
|
diagnostics: readonly ts.Diagnostic[]
|
|
|
|
): Diagnostic {
|
|
|
|
const items: DiagnosticItem[] = [];
|
|
|
|
for (const sourceDiagnostic of diagnostics) {
|
|
|
|
const item: DiagnosticItem = parseDiagnostic(sourceDiagnostic);
|
|
|
|
if (sourceDiagnostic.relatedInformation) {
|
|
|
|
item.relatedInformation = parseRelatedInformation(
|
|
|
|
sourceDiagnostic.relatedInformation
|
|
|
|
);
|
|
|
|
}
|
|
|
|
items.push(item);
|
|
|
|
}
|
|
|
|
return { items };
|
|
|
|
}
|