import { spawn } from 'child_process' ; import pick from 'lodash.pick' ; import npmRunPath from 'npm-run-path' ; import os from 'os' ; import { ConfigEnv , Plugin } from 'vite' ; import { Checker } from './Checker' ; import { OverlayErrorAction , BuildInCheckerNames , ServeAndBuildChecker , UserPluginConfig , SharedConfig , BuildCheckBinStr , PluginConfig , ACTION_TYPES , } from './types' ; export * from './types' ; export * from './codeFrame' ; export * from './worker' ; export { Checker } from './Checker' ; const sharedConfigKeys = [ 'enableBuild' , 'overlay' ] ; const buildInCheckerKeys = [ 'typescript' , 'vueTsc' , 'vls' , 'eslint' ] ; function createCheckers ( userConfig , env ) { const serveAndBuildCheckers = [ ] ; const sharedConfig = pick ( userConfig , sharedConfigKeys ) ; buildInCheckerKeys . forEach ( ( name ) => { if ( ! userConfig [ name ] ) return ; const { createServeAndBuild } = require ( `./checkers/${ name }/main` ) ; serveAndBuildCheckers . push ( createServeAndBuild ( { [ name ] : userConfig [ name ] , ... sharedConfig } , env ) ) ; } ) ; return serveAndBuildCheckers ; } export default function Plugin ( userConfig ) { const enableBuild = userConfig ?. enableBuild ?? true ; let checkers = [ ] ; let viteMode ; return { name : 'vite-plugin-checker' , config : ( config , env ) => { viteMode = env . command ; checkers = createCheckers ( userConfig || { } , env ) ; if ( viteMode !== 'serve' ) return ; checkers . forEach ( ( checker ) => { const workerConfig = checker . serve . config ; workerConfig ( { hmr : config . server ?. hmr , env , } ) ; } ) ; } , buildEnd ( ) { if ( viteMode === 'serve' ) { checkers . forEach ( ( checker ) => { const { worker } = checker . serve ; worker . terminate ( ) ; } ) ; } } , buildStart : ( ) => { if ( viteMode !== 'build' ) return ; if ( ! enableBuild ) return ; const localEnv = npmRunPath . env ( { env : process . env , cwd : process . cwd ( ) , execPath : process . execPath , } ) ; ( async ( ) => { const exitCodes = await Promise . all ( checkers . map ( ( checker ) => spawnChecker ( checker , userConfig , localEnv ) ) ) ; const exitCode = exitCodes . find ( ( code ) => code !== 0 ) ?? 0 ; if ( exitCode !== 0 ) process . exit ( exitCode ) ; } ) ( ) ; } , configureServer ( server ) { let connectedTimes = 0 ; let latestOverlayErrors = new Array ( checkers . length ) ; checkers . forEach ( ( checker , index ) => { const { worker , configureServer : workerConfigureServer } = checker . serve ; workerConfigureServer ( { root : server . config . root } ) ; worker . on ( 'message' , ( action ) => { if ( action . type === ACTION_TYPES . overlayError ) { latestOverlayErrors [ index ] = action . payload ; if ( action . payload ) { server . ws . send ( action . payload ) ; } } else if ( action . type === ACTION_TYPES . console ) { Checker . log ( action ) ; } } ) ; } ) ; return ( ) => { server . ws . on ( 'connection' , ( ) => { connectedTimes ++ ; const latestOverlayError = latestOverlayErrors . filter ( Boolean ) . slice ( - 1 ) [ 0 ] ; if ( connectedTimes > 1 && latestOverlayError ) { server . ws . send ( latestOverlayError ) ; } } ) ; server . middlewares . use ( ( req , res , next ) => { next ( ) ; } ) ; } ; } , } ; } function spawnChecker ( checker , userConfig , localEnv ) { return new Promise < number > ( ( resolve ) => { const buildBin = checker . build . buildBin ; const finalBin = typeof buildBin === 'function' ? buildBin ( userConfig ) : buildBin ; const proc = spawn ( ... finalBin , { cwd : process . cwd ( ) , stdio : 'inherit' , env : localEnv , shell : os . platform ( ) === 'win32' , } ) ; proc . on ( 'exit' , ( code ) => { if ( code !== null && code !== 0 ) { resolve ( code ) ; } else { resolve ( 0 ) ; } } ) ; } ) ; }