Skip to content

Commit

Permalink
feat: tsc use unified logger
Browse files Browse the repository at this point in the history
  • Loading branch information
fi3ework committed Jul 8, 2021
1 parent e3924f0 commit eef3d77
Show file tree
Hide file tree
Showing 4 changed files with 180 additions and 22 deletions.
15 changes: 6 additions & 9 deletions packages/vite-plugin-checker/src/checkers/tsc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import ts from 'typescript'
import { ErrorPayload } from 'vite'
import { isMainThread, parentPort } from 'worker_threads'

import { ensureCall, formatHost, tsDiagnosticToViteError } from '../utils'
import { ensureCall } from '../utils'
import { normalizeDiagnostic, diagnosticToViteError, diagnosticToTerminalLog } from '../logger'
import { createScript } from '../worker'

import type { CreateDiagnostic, PluginConfig } from '../types'
Expand Down Expand Up @@ -52,16 +53,12 @@ const createDiagnostic: CreateDiagnostic<Pick<PluginConfig, 'typescript'>> = (ch

// https://github.com/microsoft/TypeScript/blob/a545ab1ac2cb24ff3b1aaf0bfbfb62c499742ac2/src/compiler/watch.ts#L12-L28
const reportDiagnostic = (diagnostic: ts.Diagnostic) => {
const formattedDiagnostics = ts.formatDiagnosticsWithColorAndContext(
[diagnostic],
formatHost
)

const formattedDiagnostics = normalizeDiagnostic(diagnostic)
if (!currErr) {
currErr = tsDiagnosticToViteError(diagnostic)
currErr = diagnosticToViteError(formattedDiagnostics)
}

logChunk.diagnostics = formattedDiagnostics
logChunk.diagnostics = diagnosticToTerminalLog(formattedDiagnostics)
}

const reportWatchStatusChanged: ts.WatchStatusReporter = (
Expand Down Expand Up @@ -99,7 +96,7 @@ const createDiagnostic: CreateDiagnostic<Pick<PluginConfig, 'typescript'>> = (ch
if (errorCount === 0) {
logChunk.diagnostics = null
}
const d = logChunk.diagnostics === null ? '' : logChunk.diagnostics + os.EOL
const d = logChunk.diagnostics === null ? '' : logChunk.diagnostics
console.log(d + logChunk.message)
})
}
Expand Down
163 changes: 163 additions & 0 deletions packages/vite-plugin-checker/src/logger.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,163 @@
import { readFileSync } from 'fs'
import os from 'os'
import strip from 'strip-ansi'
import { ErrorPayload } from 'vite'
import chalk from 'chalk'

import { codeFrameColumns, SourceLocation } from '@babel/code-frame'

import { uriToAbsPath } from './utils'

// TODO: remove ./codeFrame.ts and ./utils.ts

import type {
Diagnostic as TsDiagnostic,
flattenDiagnosticMessageText as flattenDiagnosticMessageTextType,
LineAndCharacter,
} from 'typescript'

interface NormalizedDiagnostic {
/** error message */
message?: string
/** error conclusion */
conclusion?: string
/** error stack */
stack?: string | string[]
/** file name */
id?: string
/** checker diagnostic source */
checker: string
/** raw code frame generated by @babel/code-frame */
codeFrame?: string
/** code frame, but striped */
stripedCodeFrame?: string
/** error code location */
loc?: SourceLocation
/** error level */
level?: 'error' | 'warning'
}

export function diagnosticToTerminalLog(d: NormalizedDiagnostic): string {
const level =
d.level === 'error'
? chalk.red.bold('ERROR') + ' '
: d.level === 'warning'
? chalk.yellow.bold('WARNING') + ' '
: ''
const file = chalk.green.bold('FILE') + ' '
return [level + d.message, file + d.id, d.codeFrame + os.EOL, d.conclusion]
.filter(Boolean)
.join(os.EOL)
}

export function diagnosticToViteError(d: NormalizedDiagnostic): ErrorPayload['err'] {
let loc: ErrorPayload['err']['loc']
if (d.loc) {
loc = {
file: d.id,
line: d.loc.start.line + 1,
column: typeof d.loc.start.column === 'number' ? d.loc.start.column + 1 : 0,
}
}

return {
message: d.message ?? '',
stack:
typeof d.stack === 'string' ? d.stack : Array.isArray(d.stack) ? d.stack.join(os.EOL) : '',
id: d.id,
frame: d.codeFrame,
plugin: `vite-plugin-checker(${d.checker})`,
loc,
}
}

export function createFrame({
source,
location,
}: {
/** file source code */
source: string
location: SourceLocation
}) {
const frame = codeFrameColumns(source, location, {
// worker tty did not fork parent process stdout, let's make a workaround
forceColor: true,
})
.split('\n')
.map((line) => ' ' + line)
.join(os.EOL)

return frame
}

export function tsLocationToBabelLocation(
tsLoc: Record<'start' | 'end', LineAndCharacter /** 0-based */>
): SourceLocation {
return {
start: { line: tsLoc.start.line + 1, column: tsLoc.start.character + 1 },
end: { line: tsLoc.end.line + 1, column: tsLoc.end.character + 1 },
}
}

export function normalizeDiagnostic(d: TsDiagnostic): NormalizedDiagnostic {
if (isTsDiagnostic(d)) return normalizeTsDiagnostic(d)
throw Error(`unsupported diagnostic, only support TypeScript / VLS for now.`)
}

/* ------------------------------- TypeScript ------------------------------- */

export function isTsDiagnostic(d: any): d is TsDiagnostic {
return (
'category' in d &&
'code' in d &&
'file' in d &&
'start' in d &&
'length' in d &&
'messageText' in d
)
}

export function normalizeTsDiagnostic(d: TsDiagnostic): NormalizedDiagnostic {
const fileName = d.file?.fileName
const {
flattenDiagnosticMessageText,
}: {
flattenDiagnosticMessageText: typeof flattenDiagnosticMessageTextType
} = require('typescript')

const message = flattenDiagnosticMessageText(d.messageText, os.EOL)

let loc: SourceLocation | undefined
const pos = d.start === undefined ? null : d.file?.getLineAndCharacterOfPosition(d.start)
if (pos && d.file && typeof d.start === 'number' && typeof d.length === 'number') {
loc = tsLocationToBabelLocation({
start: d.file?.getLineAndCharacterOfPosition(d.start),
end: d.file?.getLineAndCharacterOfPosition(d.start + d.length),
})
}

let codeFrame: string | undefined
if (loc) {
codeFrame = createFrame({
source: d.file!.text,
location: loc,
})
}

return {
message,
conclusion: '',
codeFrame,
stripedCodeFrame: codeFrame && strip(codeFrame),
id: fileName,
checker: 'TypeScript',
loc,
level: 'error',
}
}

/* ----------------------------------- VLS ---------------------------------- */

/* --------------------------------- vue-tsc -------------------------------- */

/* --------------------------------- ESLint --------------------------------- */
23 changes: 11 additions & 12 deletions packages/vite-plugin-checker/src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,26 +35,25 @@ export function tsDiagnosticToViteError(d: ts.Diagnostic): ErrorPayload['err'] {
// has detail message
if (d.file && typeof d.start === 'number' && typeof d.length === 'number') {
return {
message: strip(
message:
ts.flattenDiagnosticMessageText(d.messageText, formatHost.getNewLine()) +
os.EOL +
os.EOL +
createFrame({
source: d.file!.text,
location: tsLocationToBabelLocation({
start: d.file?.getLineAndCharacterOfPosition(d.start),
end: d.file?.getLineAndCharacterOfPosition(d.start + d.length),
}),
})
),
os.EOL +
os.EOL +
createFrame({
source: d.file!.text,
location: tsLocationToBabelLocation({
start: d.file?.getLineAndCharacterOfPosition(d.start),
end: d.file?.getLineAndCharacterOfPosition(d.start + d.length),
}),
}),
stack: '',
id: d.file?.fileName,
plugin: 'vite-plugin-checker',
loc,
}
}

// no detail message
// ops, no detailed message
return {
message: ts.flattenDiagnosticMessageText(d.messageText, formatHost.getNewLine()),
stack: '',
Expand Down
1 change: 0 additions & 1 deletion packages/vite-plugin-checker/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ export function createScript<T>({
env: ConfigEnv
): ConfigureServeChecker => {
const isBuild = env.command === 'build'

const worker = new Worker(absFilename, {
workerData: { env, checkerConfig, columns: process.stdout.columns },
})
Expand Down

0 comments on commit eef3d77

Please sign in to comment.