Skip to content

Commit

Permalink
feat: tsc checker extends base Checker
Browse files Browse the repository at this point in the history
  • Loading branch information
fi3ework committed Jul 17, 2021
1 parent 23c71c0 commit 8823409
Show file tree
Hide file tree
Showing 4 changed files with 203 additions and 2 deletions.
64 changes: 64 additions & 0 deletions packages/vite-plugin-checker/src/Checker.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import invariant from 'tiny-invariant'
import { ErrorPayload } from 'vite'
import { isMainThread } from 'worker_threads'

import { createScript, Script } from './worker'

import type { CreateDiagnostic, PluginConfig, BuildCheckBin } from './types'

export interface CheckerAbility {
sealConclusion: any
}

export abstract class Checker<T = any> {
public buildBin: BuildCheckBin
public name: string
public absFilePath: string
public createDiagnostic: CreateDiagnostic
public script?: Script<any>

public constructor({
name,
absFilePath,
buildBin,
createDiagnostic,
}: {
name: string
absFilePath: string
buildBin: BuildCheckBin
createDiagnostic: CreateDiagnostic
}) {
this.name = name
this.absFilePath = absFilePath
this.buildBin = buildBin
this.createDiagnostic = createDiagnostic
}

public prepare() {
const script = createScript<Pick<PluginConfig, 'typescript'>>({
absFilename: this.absFilePath,
buildBin: this.buildBin,
serverChecker: { createDiagnostic: this.createDiagnostic },
})!

this.script = script
return script
}

public initMainThread() {
invariant(this.script, `script should be created in 'prepare', but got ${this.script}`)

if (isMainThread) {
const createServeAndBuild = this.script.mainScript()
return createServeAndBuild
}
}

public initWorkerThread() {
invariant(this.script, `script should be created in 'prepare', but got ${this.script}`)

if (!isMainThread) {
this.script.workerScript()
}
}
}
135 changes: 135 additions & 0 deletions packages/vite-plugin-checker/src/checkers/tsc2.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
import os from 'os'
import invariant from 'tiny-invariant'
import ts from 'typescript'
import { isMainThread, parentPort } from 'worker_threads'

import { Checker, CheckerAbility } from '../Checker'
import { diagnosticToTerminalLog, diagnosticToViteError, normalizeDiagnostic } from '../logger'
import { ensureCall } from '../utils'

import type { CreateDiagnostic, PluginConfig, BuildCheckBin } from '../types'
import type { ErrorPayload } from 'vite'

const createDiagnostic: CreateDiagnostic<Pick<PluginConfig, 'typescript'>> = (checkerConfig) => {
let overlay = true // Vite defaults to true
let currErr: ErrorPayload['err'] | null = null

return {
config: ({ hmr }) => {
const viteOverlay = !(typeof hmr === 'object' && hmr.overlay === false)

if (checkerConfig.overlay === false || !viteOverlay) {
overlay = false
}
},
configureServer({ root }) {
invariant(checkerConfig.typescript, 'config.typescript should be `false`')
const finalConfig =
checkerConfig.typescript === true
? { root, tsconfigPath: 'tsconfig.json' }
: {
root: checkerConfig.typescript.root ?? root,
tsconfigPath: checkerConfig.typescript.tsconfigPath ?? 'tsconfig.json',
}

let configFile: string | undefined

configFile = ts.findConfigFile(finalConfig.root, ts.sys.fileExists, finalConfig.tsconfigPath)

if (configFile === undefined) {
throw Error(
`Failed to find a valid tsconfig.json: ${finalConfig.tsconfigPath} at ${finalConfig.root} is not a valid tsconfig`
)
}

let logChunk = ''

// https://github.com/microsoft/TypeScript/blob/a545ab1ac2cb24ff3b1aaf0bfbfb62c499742ac2/src/compiler/watch.ts#L12-L28
const reportDiagnostic = (diagnostic: ts.Diagnostic) => {
const formattedDiagnostics = normalizeDiagnostic(diagnostic)
if (!currErr) {
currErr = diagnosticToViteError(formattedDiagnostics)
}

logChunk += os.EOL + diagnosticToTerminalLog(formattedDiagnostics)
}

const reportWatchStatusChanged: ts.WatchStatusReporter = (
diagnostic,
newLine,
options,
errorCount
// eslint-disable-next-line max-params
) => {
if (diagnostic.code === 6031) return
// https://github.com/microsoft/TypeScript/issues/32542
// https://github.com/microsoft/TypeScript/blob/dc237b317ed4bbccd043ddda802ffde00362a387/src/compiler/diagnosticMessages.json#L4086-L4088
switch (diagnostic.code) {
case 6031:
case 6032:
// clear current error and use the newer errors
currErr = null
return
case 6193: // 1 Error
case 6194: // 0 errors or 2+ errors
if (currErr && overlay) {
parentPort?.postMessage({
type: 'ERROR',
payload: {
type: 'error',
err: currErr,
},
})
}
}

ensureCall(() => {
if (errorCount === 0) {
logChunk = ''
}

console.log(logChunk + (errorCount ? os.EOL : '') + diagnostic.messageText.toString())
})
}

// https://github.com/microsoft/TypeScript/issues/32385
// https://github.com/microsoft/TypeScript/pull/33082/files
const createProgram = ts.createEmitAndSemanticDiagnosticsBuilderProgram

const host = ts.createWatchCompilerHost(
configFile,
{ noEmit: true },
ts.sys,
createProgram,
reportDiagnostic,
reportWatchStatusChanged
)

ts.createWatchProgram(host)
},
}
}

export class TscChecker extends Checker implements CheckerAbility {
public constructor() {
super({
name: 'typescript',
absFilePath: __filename,
buildBin: ['tsc', ['--noEmit']],
createDiagnostic,
})
}

public sealConclusion() {}

public init() {
const createServeAndBuild = super.initMainThread()
module.exports.createServeAndBuild = createServeAndBuild

super.initWorkerThread()
}
}

const tscChecker = new TscChecker()
tscChecker.prepare()
tscChecker.init()
4 changes: 3 additions & 1 deletion packages/vite-plugin-checker/src/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@ import os from 'os'
import invariant from 'tiny-invariant'
import { ConfigEnv, Plugin } from 'vite'

import type { TscChecker as TscCheckerType } from './checkers/tsc2'

import type {
OverlayErrorAction,
BuildInCheckers,
Expand All @@ -30,7 +32,7 @@ function createCheckers(userConfig: UserPluginConfig, env: ConfigEnv): ServeAndB

if (typescript) {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { createServeAndBuild } = require('./checkers/tsc')
const { createServeAndBuild } = require('./checkers/tsc2')
serveAndBuildCheckers.push(createServeAndBuild({ typescript, ...sharedConfig }, env))
}

Expand Down
2 changes: 1 addition & 1 deletion packages/vite-plugin-checker/src/worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ interface WorkerScriptOptions {
serverChecker: ServeChecker
}

interface Script<T> {
export interface Script<T> {
mainScript: () => (config: T & SharedConfig, env: ConfigEnv) => ServeAndBuildChecker
workerScript: () => void
}
Expand Down

0 comments on commit 8823409

Please sign in to comment.