diff --git a/README.md b/README.md index c4365283..30dd8237 100644 --- a/README.md +++ b/README.md @@ -126,9 +126,9 @@ export default { ```ts { /** - * Show overlay on UI view when there are errors or warnings - * - Set `true` to show overlay in dev mode - * - Set `false` to disable overlay in dev mode + * Show overlay on UI view when there are errors or warnings in dev mode. + * - Set `true` to show overlay + * - Set `false` to disable overlay * - Set with a object to customize overlay * * @defaultValue `true` @@ -137,7 +137,7 @@ export default { | boolean | { /** - * Set this true if you want the overlay to default to being open if errors/warnings are found. + * Set this true if you want the overlay to default to being open if errors/warnings are found * @defaultValue `true` */ initialIsOpen?: boolean @@ -152,6 +152,14 @@ export default { */ badgeStyle?: string } + /** + * stdout in terminal which starts the Vite server in dev mode. + * - Set `true` to enable + * - Set `false` to disable + * + * @defaultValue `true` + */ + terminal: boolean /** * Enable checking in build mode * @defaultValue `true` diff --git a/packages/vite-plugin-checker/src/checkers/eslint/main.ts b/packages/vite-plugin-checker/src/checkers/eslint/main.ts index ea1fcf56..89af483e 100644 --- a/packages/vite-plugin-checker/src/checkers/eslint/main.ts +++ b/packages/vite-plugin-checker/src/checkers/eslint/main.ts @@ -24,10 +24,12 @@ const manager = new FileDiagnosticManager() import type { CreateDiagnostic } from '../../types' const createDiagnostic: CreateDiagnostic<'eslint'> = (pluginConfig) => { let overlay = true + let terminal = true return { - config: async ({ enableOverlay }) => { + config: async ({ enableOverlay, enableTerminal }) => { overlay = enableOverlay + terminal = enableTerminal }, async configureServer({ root }) { if (!pluginConfig.eslint) return @@ -56,9 +58,11 @@ const createDiagnostic: CreateDiagnostic<'eslint'> = (pluginConfig) => { const dispatchDiagnostics = () => { const diagnostics = filterLogLevel(manager.getDiagnostics(), logLevel) - diagnostics.forEach((d) => { - consoleLog(diagnosticToTerminalLog(d, 'ESLint')) - }) + if (terminal) { + diagnostics.forEach((d) => { + consoleLog(diagnosticToTerminalLog(d, 'ESLint')) + }) + } if (overlay) { parentPort?.postMessage({ diff --git a/packages/vite-plugin-checker/src/checkers/typescript/main.ts b/packages/vite-plugin-checker/src/checkers/typescript/main.ts index 0c9bfb32..bd41386a 100644 --- a/packages/vite-plugin-checker/src/checkers/typescript/main.ts +++ b/packages/vite-plugin-checker/src/checkers/typescript/main.ts @@ -17,11 +17,13 @@ import { ACTION_TYPES, CreateDiagnostic, DiagnosticLevel, DiagnosticToRuntime } const createDiagnostic: CreateDiagnostic<'typescript'> = (pluginConfig) => { let overlay = true + let terminal = true let currDiagnostics: DiagnosticToRuntime[] = [] return { - config: async ({ enableOverlay }) => { + config: async ({ enableOverlay, enableTerminal }) => { overlay = enableOverlay + terminal = enableTerminal }, configureServer({ root }) { invariant(pluginConfig.typescript, 'config.typescript should be `false`') @@ -89,7 +91,9 @@ const createDiagnostic: CreateDiagnostic<'typescript'> = (pluginConfig) => { logChunk = '' } - consoleLog(logChunk + os.EOL + diagnostic.messageText.toString()) + if (terminal) { + consoleLog(logChunk + os.EOL + diagnostic.messageText.toString()) + } }) } diff --git a/packages/vite-plugin-checker/src/checkers/vls/diagnostics.ts b/packages/vite-plugin-checker/src/checkers/vls/diagnostics.ts index 5aaec922..be3000d0 100644 --- a/packages/vite-plugin-checker/src/checkers/vls/diagnostics.ts +++ b/packages/vite-plugin-checker/src/checkers/vls/diagnostics.ts @@ -27,13 +27,12 @@ import { import { URI } from 'vscode-uri' import { - consoleLog, diagnosticToTerminalLog, - diagnosticToRuntimeError, normalizeLspDiagnostic, normalizePublishDiagnosticParams, + NormalizedDiagnostic, } from '../../logger' -import { DeepPartial, DiagnosticToRuntime } from '../../types' +import { DeepPartial } from '../../types' import { getInitParams, VlsOptions } from './initParams' import { FileDiagnosticManager } from '../../FileDiagnosticManager' @@ -58,7 +57,8 @@ export interface DiagnosticOptions { watch: boolean verbose: boolean config: DeepPartial | null - errorCallback?: (diagnostic: PublishDiagnosticsParams, viteError: DiagnosticToRuntime[]) => void + onDispatch?: (normalized: NormalizedDiagnostic[]) => void + onDispatchInitialSummary?: (errorCount: number) => void } export async function diagnostics( @@ -66,7 +66,7 @@ export async function diagnostics( logLevel: LogLevel, options: DiagnosticOptions = { watch: false, verbose: false, config: null } ) { - const { watch, errorCallback } = options + const { watch, onDispatch } = options if (options.verbose) { console.log('====================================') console.log('Getting Vetur diagnostics') @@ -88,19 +88,10 @@ export async function diagnostics( console.log('====================================') } - // initial report - if (!errCount) { - vlsConsoleLog(chalk.green(`[VLS checker] No error found`)) - if (!watch) { - process.exit(0) - } - } else { - vlsConsoleLog( - chalk.red(`[VLS checker] Found ${errCount} ${errCount === 1 ? 'error' : 'errors'}`) - ) - if (!watch) { - process.exit(1) - } + // dispatch error summary in build mode + if (!options.watch && typeof errCount === 'number') { + options?.onDispatchInitialSummary?.(errCount) + process.exit(errCount > 0 ? 1 : 0) } } @@ -119,18 +110,15 @@ export class TestStream extends Duplex { public _read(_size: number) {} } -let vlsConsoleLog = consoleLog - function suppressConsole() { let disposed = false - const rawConsoleLog = vlsConsoleLog - vlsConsoleLog = () => {} + const rawConsoleLog = console.log + console.log = () => {} return () => { if (disposed) return - disposed = true - vlsConsoleLog = rawConsoleLog + console.log = rawConsoleLog } } @@ -166,15 +154,8 @@ export async function prepareClientConnection( const nextDiagnosticInFile = await normalizePublishDiagnosticParams(publishDiagnostics) fileDiagnosticManager.updateByFileId(absFilePath, nextDiagnosticInFile) - const diagnostics = fileDiagnosticManager.getDiagnostics() - - vlsConsoleLog(os.EOL) - vlsConsoleLog(diagnostics.map((d) => diagnosticToTerminalLog(d, 'VLS')).join(os.EOL)) - - if (diagnostics) { - const normalized = diagnosticToRuntimeError(diagnostics) - options.errorCallback?.(publishDiagnostics, normalized) - } + const normalized = fileDiagnosticManager.getDiagnostics() + options.onDispatch?.(normalized) } const vls = new VLS(serverConnection as any) @@ -232,7 +213,7 @@ async function getDiagnostics( workspaceUri: URI, severity: DiagnosticSeverity, options: DiagnosticOptions -) { +): Promise { const { clientConnection } = await prepareClientConnection(workspaceUri, severity, options) const files = glob.sync([...watchedDidChangeContentGlob, ...watchedDidChangeWatchedFilesGlob], { @@ -241,7 +222,7 @@ async function getDiagnostics( }) if (files.length === 0) { - console.log('No input files') + console.log('[VLS checker] No input files') return 0 } @@ -252,17 +233,15 @@ async function getDiagnostics( const absFilePaths = files.map((f) => path.resolve(workspaceUri.fsPath, f)) - // initial diagnostics report - // watch mode will run this full diagnostic at starting - let initialErrCount = 0 - let logChunk = '' - - if (options.watch) { - disposeSuppressConsole = suppressConsole() - } + // VLS will stdout verbose log, suppress console before any serverConnection + disposeSuppressConsole = suppressConsole() + let initialErrCount = 0 await Promise.all( absFilePaths.map(async (absFilePath) => { + // serve mode - step 1 + // build mode - step 1 + // report all existing files from client side to server with type `DidOpenTextDocumentNotification.type` const fileText = await fs.promises.readFile(absFilePath, 'utf-8') clientConnection.sendNotification(DidOpenTextDocumentNotification.type, { textDocument: { @@ -273,7 +252,8 @@ async function getDiagnostics( }, }) - // log in build mode + // build mode - step 2 + // use $/getDiagnostics to get diagnostics from server side directly if (!options.watch) { try { let diagnostics = (await clientConnection.sendRequest('$/getDiagnostics', { @@ -282,7 +262,7 @@ async function getDiagnostics( })) as Diagnostic[] diagnostics = filterDiagnostics(diagnostics, severity) - + let logChunk = '' if (diagnostics.length > 0) { logChunk += os.EOL + @@ -305,49 +285,69 @@ async function getDiagnostics( } }) } + + console.log(logChunk) + return initialErrCount } catch (err) { console.error(err.stack) + return initialErrCount } } }) ) - // watched diagnostics report - if (options.watch) { - const watcher = chokidar.watch([], { - ignored: (path: string) => path.includes('node_modules'), - }) + if (!options.watch) { + return initialErrCount + } - watcher.add(workspaceUri.fsPath) - watcher.on('all', async (event, filePath) => { - const extname = path.extname(filePath) - // .vue file changed - if (!filePath.endsWith('.vue')) return - const fileContent = await fs.promises.readFile(filePath, 'utf-8') - clientConnection.sendNotification(DidChangeTextDocumentNotification.type, { + // serve mode - step 2 + // watch files (.vue,.js,.ts,.json) change and send notification to server + await Promise.all( + absFilePaths.map(async (absFilePath) => { + const fileText = await fs.promises.readFile(absFilePath, 'utf-8') + clientConnection.sendNotification(DidOpenTextDocumentNotification.type, { textDocument: { - uri: URI.file(filePath).toString(), - version: Date.now(), + languageId: 'vue', + uri: URI.file(absFilePath).toString(), + version: DOC_VERSION.init, + text: fileText, }, - contentChanges: [{ text: fileContent }], }) + }) + ) - // .js,.ts,.json file changed - if (watchedDidChangeWatchedFiles.includes(extname)) { - clientConnection.sendNotification(DidChangeWatchedFilesNotification.type, { - changes: [ - { - uri: URI.file(filePath).toString(), - type: event === 'add' ? 1 : event === 'unlink' ? 3 : 2, - }, - ], - }) - } + const watcher = chokidar.watch([], { + ignored: (path: string) => path.includes('node_modules'), + }) + + watcher.add(workspaceUri.fsPath) + watcher.on('all', async (event, filePath) => { + const extname = path.extname(filePath) + // .vue file changed + if (!filePath.endsWith('.vue')) return + const fileContent = await fs.promises.readFile(filePath, 'utf-8') + clientConnection.sendNotification(DidChangeTextDocumentNotification.type, { + textDocument: { + uri: URI.file(filePath).toString(), + version: Date.now(), + }, + contentChanges: [{ text: fileContent }], }) - } - vlsConsoleLog(logChunk) - return initialErrCount + // .js,.ts,.json file changed + if (watchedDidChangeWatchedFiles.includes(extname)) { + clientConnection.sendNotification(DidChangeWatchedFilesNotification.type, { + changes: [ + { + uri: URI.file(filePath).toString(), + type: event === 'add' ? 1 : event === 'unlink' ? 3 : 2, + }, + ], + }) + } + }) + + return null } function isObject(item: any): item is {} { diff --git a/packages/vite-plugin-checker/src/checkers/vls/main.ts b/packages/vite-plugin-checker/src/checkers/vls/main.ts index 01b4faa6..2a171147 100644 --- a/packages/vite-plugin-checker/src/checkers/vls/main.ts +++ b/packages/vite-plugin-checker/src/checkers/vls/main.ts @@ -1,34 +1,66 @@ -import { parentPort, workerData } from 'worker_threads' +import chalk from 'chalk' +import os from 'os' +import { parentPort } from 'worker_threads' import { Checker } from '../../Checker' +import { + consoleLog, + diagnosticToRuntimeError, + diagnosticToTerminalLog, + toViteCustomPayload, +} from '../../logger' import { ACTION_TYPES } from '../../types' import { DiagnosticOptions, diagnostics } from './diagnostics' -import { toViteCustomPayload } from '../../logger' + +import type { ConfigEnv } from 'vite' import type { CreateDiagnostic } from '../../types' export const createDiagnostic: CreateDiagnostic<'vls'> = (pluginConfig) => { let overlay = true + let terminal = true + let command: ConfigEnv['command'] return { - config: ({ enableOverlay }) => { + config: ({ enableOverlay, enableTerminal, env }) => { overlay = enableOverlay + terminal = enableTerminal + command = env.command }, async configureServer({ root }) { const workDir: string = root - const errorCallback: DiagnosticOptions['errorCallback'] = (diagnostics, overlayErr) => { - if (!overlay) return - parentPort?.postMessage({ - type: ACTION_TYPES.overlayError, - payload: toViteCustomPayload('vls', overlayErr ? overlayErr : []), - }) + + const onDispatch: DiagnosticOptions['onDispatch'] = (normalized) => { + if (overlay && command === 'serve') { + parentPort?.postMessage({ + type: ACTION_TYPES.overlayError, + payload: toViteCustomPayload('vls', diagnosticToRuntimeError(normalized)), + }) + } + + if (terminal) { + consoleLog(normalized.map((d) => diagnosticToTerminalLog(d, 'VLS')).join(os.EOL)) + } + } + + const onDispatchInitialSummary: DiagnosticOptions['onDispatchInitialSummary'] = ( + errCount + ) => { + if (!errCount) { + consoleLog(chalk.green(`[VLS checker] No error found`)) + } else { + consoleLog( + chalk.red(`[VLS checker] Found ${errCount} ${errCount === 1 ? 'error' : 'errors'}`) + ) + } } - const vlsConfig = workerData?.checkerConfig?.vls + const vlsConfig = pluginConfig?.vls await diagnostics(workDir, 'WARN', { - errorCallback, + onDispatch, + onDispatchInitialSummary, watch: true, verbose: false, - config: typeof vlsConfig === 'object' ? vlsConfig : undefined, + config: typeof vlsConfig === 'object' ? vlsConfig : null, }) }, } diff --git a/packages/vite-plugin-checker/src/main.ts b/packages/vite-plugin-checker/src/main.ts index d944f388..4f302973 100644 --- a/packages/vite-plugin-checker/src/main.ts +++ b/packages/vite-plugin-checker/src/main.ts @@ -48,6 +48,7 @@ function createCheckers(userConfig: UserPluginConfig, env: ConfigEnv): ServeAndB export default function Plugin(userConfig: UserPluginConfig): Plugin { const enableBuild = userConfig?.enableBuild ?? true const enableOverlay = userConfig?.overlay !== false + const enableTerminal = userConfig?.terminal !== false const overlayConfig = typeof userConfig?.overlay === 'object' ? userConfig?.overlay : null let checkers: ServeAndBuildChecker[] = [] let viteMode: ConfigEnv['command'] | undefined @@ -65,6 +66,7 @@ export default function Plugin(userConfig: UserPluginConfig): Plugin { const workerConfig = checker.serve.config workerConfig({ enableOverlay, + enableTerminal, env, }) }) diff --git a/packages/vite-plugin-checker/src/types.ts b/packages/vite-plugin-checker/src/types.ts index 217ecfa2..9a7e063d 100644 --- a/packages/vite-plugin-checker/src/types.ts +++ b/packages/vite-plugin-checker/src/types.ts @@ -73,14 +73,9 @@ export interface DiagnosticToRuntime extends ErrorPayloadErr { /** checkers shared configuration */ export interface SharedConfig { /** - * Enable checking in build mode - * @defaultValue `true` - */ - enableBuild: boolean - /** - * Show overlay on UI view when there are errors or warnings - * - Set `true` to show overlay in dev mode - * - Set `false` to disable overlay in dev mode + * Show overlay on UI view when there are errors or warnings in dev mode. + * - Set `true` to show overlay + * - Set `false` to disable overlay * - Set with a object to customize overlay * * @defaultValue `true` @@ -89,7 +84,7 @@ export interface SharedConfig { | boolean | { /** - * Set this true if you want the overlay to default to being open if errors/warnings are found. + * Set this true if you want the overlay to default to being open if errors/warnings are found * @defaultValue `true` */ initialIsOpen?: boolean @@ -104,6 +99,19 @@ export interface SharedConfig { */ badgeStyle?: string } + /** + * stdout in terminal which starts the Vite server in dev mode. + * - Set `true` to enable + * - Set `false` to disable + * + * @defaultValue `true` + */ + terminal: boolean + /** + * Enable checking in build mode + * @defaultValue `true` + */ + enableBuild: boolean } export interface BuildInCheckers { @@ -146,6 +154,7 @@ export interface OverlayErrorAction extends Action { interface ConfigActionPayload { enableOverlay: boolean + enableTerminal: boolean env: ConfigEnv } diff --git a/playground/vue2-vls/__tests__/test.spec.ts b/playground/vue2-vls/__tests__/test.spec.ts index 8f162aa6..3050880b 100644 --- a/playground/vue2-vls/__tests__/test.spec.ts +++ b/playground/vue2-vls/__tests__/test.spec.ts @@ -80,7 +80,7 @@ describe('vue2-vls', () => { it('enableBuild: false', async () => { editFile('vite.config.ts', (code) => - code.replace('checker({ vls: {} })', 'checker({ vls: {}, enableBuild: false })') + code.replace('checker({ vls: true })', 'checker({ vls: true, enableBuild: false })') ) await viteBuild({ diff --git a/playground/vue2-vls/vite.config.ts b/playground/vue2-vls/vite.config.ts index 84fe8be8..10ebd004 100644 --- a/playground/vue2-vls/vite.config.ts +++ b/playground/vue2-vls/vite.config.ts @@ -14,7 +14,7 @@ const config = defineConfig({ build: { minify: true, }, - plugins: [createVuePlugin({}), ViteComponents({ transformer: 'vue2' }), checker({ vls: {} })], + plugins: [createVuePlugin({}), ViteComponents({ transformer: 'vue2' }), checker({ vls: true })], server: { port: 3001, },