summaryrefslogtreecommitdiff
path: root/cli/js/diagnostics.ts
diff options
context:
space:
mode:
authorRyan Dahl <ry@tinyclouds.org>2019-10-04 20:28:51 -0400
committerGitHub <noreply@github.com>2019-10-04 20:28:51 -0400
commitb81e5db17aa8b3088d6034ddf86b79c69410f012 (patch)
tree579e4c23d60d1b0d038156bc28a04f74ea87b2f0 /cli/js/diagnostics.ts
parent9049213867d30f7df090a83b6baf3e0717a4d2d2 (diff)
Merge deno_cli_snapshots into deno_cli (#3064)
Diffstat (limited to 'cli/js/diagnostics.ts')
-rw-r--r--cli/js/diagnostics.ts217
1 files changed, 217 insertions, 0 deletions
diff --git a/cli/js/diagnostics.ts b/cli/js/diagnostics.ts
new file mode 100644
index 000000000..7cdb154b9
--- /dev/null
+++ b/cli/js/diagnostics.ts
@@ -0,0 +1,217 @@
+// Copyright 2018-2019 the Deno authors. All rights reserved. MIT license.
+
+// Diagnostic provides an abstraction for advice/errors received from a
+// compiler, which is strongly influenced by the format of TypeScript
+// diagnostics.
+
+/** The log category for a diagnostic message */
+export enum DiagnosticCategory {
+ Log = 0,
+ Debug = 1,
+ Info = 2,
+ Error = 3,
+ Warning = 4,
+ Suggestion = 5
+}
+
+export interface DiagnosticMessageChain {
+ message: string;
+ category: DiagnosticCategory;
+ code: number;
+ next?: DiagnosticMessageChain[];
+}
+
+export interface DiagnosticItem {
+ /** A string message summarizing the diagnostic. */
+ message: string;
+
+ /** An ordered array of further diagnostics. */
+ messageChain?: DiagnosticMessageChain;
+
+ /** Information related to the diagnostic. This is present when there is a
+ * suggestion or other additional diagnostic information */
+ relatedInformation?: DiagnosticItem[];
+
+ /** The text of the source line related to the diagnostic */
+ sourceLine?: string;
+
+ /** The line number that is related to the diagnostic */
+ lineNumber?: number;
+
+ /** The name of the script resource related to the diagnostic */
+ scriptResourceName?: string;
+
+ /** The start position related to the diagnostic */
+ startPosition?: number;
+
+ /** The end position related to the diagnostic */
+ endPosition?: number;
+
+ /** The category of the diagnostic */
+ category: DiagnosticCategory;
+
+ /** A number identifier */
+ code: number;
+
+ /** The the start column of the sourceLine related to the diagnostic */
+ startColumn?: number;
+
+ /** The end column of the sourceLine related to the diagnostic */
+ endColumn?: number;
+}
+
+export interface Diagnostic {
+ /** An array of diagnostic items. */
+ items: DiagnosticItem[];
+}
+
+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 };
+}