diff options
author | Ryan Dahl <ry@tinyclouds.org> | 2019-10-04 20:28:51 -0400 |
---|---|---|
committer | GitHub <noreply@github.com> | 2019-10-04 20:28:51 -0400 |
commit | b81e5db17aa8b3088d6034ddf86b79c69410f012 (patch) | |
tree | 579e4c23d60d1b0d038156bc28a04f74ea87b2f0 /cli/js/diagnostics.ts | |
parent | 9049213867d30f7df090a83b6baf3e0717a4d2d2 (diff) |
Merge deno_cli_snapshots into deno_cli (#3064)
Diffstat (limited to 'cli/js/diagnostics.ts')
-rw-r--r-- | cli/js/diagnostics.ts | 217 |
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 }; +} |