From 74381ccc464f7f24afef422ba9b7feee9d9da9aa Mon Sep 17 00:00:00 2001 From: sapphi-red <49056869+sapphi-red@users.noreply.github.com> Date: Mon, 9 Dec 2024 19:42:38 +0900 Subject: [PATCH] feat: support log related functions in dev --- packages/vite/rollup.dts.config.ts | 1 + .../server/__tests__/pluginContainer.spec.ts | 112 ++++++++++++++++- .../vite/src/node/server/pluginContainer.ts | 117 ++++++++++++------ 3 files changed, 189 insertions(+), 41 deletions(-) diff --git a/packages/vite/rollup.dts.config.ts b/packages/vite/rollup.dts.config.ts index e622053ba156ba..be280e237c7186 100644 --- a/packages/vite/rollup.dts.config.ts +++ b/packages/vite/rollup.dts.config.ts @@ -49,6 +49,7 @@ const identifierReplacements: Record> = { rollup: { Plugin$1: 'rollup.Plugin', PluginContext$1: 'rollup.PluginContext', + MinimalPluginContext$1: 'rollup.MinimalPluginContext', TransformPluginContext$1: 'rollup.TransformPluginContext', TransformResult$2: 'rollup.TransformResult', }, diff --git a/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts b/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts index a6491bc962a2c4..e78cf7a11b981b 100644 --- a/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts +++ b/packages/vite/src/node/server/__tests__/pluginContainer.spec.ts @@ -1,8 +1,10 @@ -import { describe, expect, it } from 'vitest' +import { stripVTControlCharacters } from 'node:util' +import { describe, expect, it, vi } from 'vitest' import type { UserConfig } from '../../config' import { resolveConfig } from '../../config' import type { Plugin } from '../../plugin' import { DevEnvironment } from '../environment' +import { createLogger } from '../../logger' describe('plugin container', () => { describe('getModuleInfo', () => { @@ -142,6 +144,55 @@ describe('plugin container', () => { }) }) + describe('options', () => { + it('should not throw errors when this.debug is called', async () => { + const plugin: Plugin = { + name: 'p1', + options() { + this.debug('test') + }, + } + await getDevEnvironment({ + plugins: [plugin], + }) + }) + + const logFunctions = ['info', 'warn'] as const + for (const logFunction of logFunctions) { + it(`should support this.${logFunction}`, async () => { + const logger = createLogger() + const mockedFn = vi + .spyOn(logger, logFunction) + .mockImplementation(() => {}) + const plugin: Plugin = { + name: 'p1', + options() { + this[logFunction]('test') + }, + } + await getDevEnvironment({ + plugins: [plugin], + customLogger: logger, + }) + expect(mockedFn).toHaveBeenCalledOnce() + }) + } + + it('should support this.error', async () => { + const plugin: Plugin = { + name: 'p1', + options() { + this.error('test') + }, + } + await expect(() => + getDevEnvironment({ + plugins: [plugin], + }), + ).rejects.toThrowError('test') + }) + }) + describe('load', () => { it('can resolve a secondary module', async () => { const entryUrl = '/x.js' @@ -212,6 +263,65 @@ describe('plugin container', () => { ) expect(result.code).equals('3') }) + + it('should not throw errors when this.debug is called', async () => { + const plugin: Plugin = { + name: 'p1', + load() { + this.debug({ message: 'test', pos: 12 }) + }, + } + const environment = await getDevEnvironment({ + plugins: [plugin], + }) + await environment.pluginContainer.load('foo') + }) + + const logFunctions = ['info', 'warn'] as const + for (const logFunction of logFunctions) { + it(`should support this.${logFunction}`, async () => { + const logger = createLogger() + const mockedFn = vi + .spyOn(logger, logFunction) + .mockImplementation(() => {}) + const plugin: Plugin = { + name: 'p1', + load() { + this[logFunction]({ message: 'test', pos: 12 }) + }, + } + const environment = await getDevEnvironment({ + plugins: [plugin], + customLogger: logger, + }) + await environment.pluginContainer.load('foo') + expect(mockedFn).toHaveBeenCalledOnce() + expect(stripVTControlCharacters(mockedFn.mock.calls[0][0])).toBe( + `${logFunction === 'warn' ? 'warning' : logFunction}: test\n` + + ' Plugin: p1', + ) + }) + } + + it('should support this.error', async () => { + const plugin: Plugin = { + name: 'p1', + load() { + this.error({ message: 'test', pos: 12 }) + }, + } + const environment = await getDevEnvironment({ + plugins: [plugin], + }) + await expect(() => environment.pluginContainer.load('foo')).rejects + .toThrowErrorMatchingInlineSnapshot(` + { + "message": "test", + "plugin": "p1", + "pos": 12, + } + `) + }) }) }) diff --git a/packages/vite/src/node/server/pluginContainer.ts b/packages/vite/src/node/server/pluginContainer.ts index c570c020a7bdfc..db656b9dab3c80 100644 --- a/packages/vite/src/node/server/pluginContainer.ts +++ b/packages/vite/src/node/server/pluginContainer.ts @@ -40,7 +40,6 @@ import type { FunctionPluginHooks, InputOptions, LoadResult, - MinimalPluginContext, ModuleInfo, ModuleOptions, NormalizedInputOptions, @@ -48,9 +47,11 @@ import type { ParallelPluginHooks, PartialNull, PartialResolvedId, + PluginContextMeta, ResolvedId, RollupError, RollupLog, + MinimalPluginContext as RollupMinimalPluginContext, PluginContext as RollupPluginContext, TransformPluginContext as RollupTransformPluginContext, SourceDescription, @@ -88,8 +89,6 @@ import type { EnvironmentModuleNode, } from './moduleGraph' -const noop = () => {} - // same default value of "moduleInfo.meta" as in Rollup const EMPTY_OBJECT = Object.freeze({}) @@ -105,6 +104,9 @@ const debugPluginResolve = createDebugger('vite:plugin-resolve', { const debugPluginTransform = createDebugger('vite:plugin-transform', { onlyWhenFocused: 'vite:plugin', }) +const debugPluginContainerContext = createDebugger( + 'vite:plugin-container-context', +) export const ERR_CLOSED_SERVER = 'ERR_CLOSED_SERVER' @@ -175,18 +177,10 @@ class EnvironmentPluginContainer { public plugins: Plugin[], public watcher?: FSWatcher, ) { - this.minimalContext = { - meta: { - rollupVersion, - watchMode: true, - }, - debug: noop, - info: noop, - warn: noop, - // @ts-expect-error noop - error: noop, + this.minimalContext = new MinimalPluginContext( + { rollupVersion, watchMode: true }, environment, - } + ) const utils = createPluginHookUtils(plugins) this.getSortedPlugins = utils.getSortedPlugins this.getSortedPluginHooks = utils.getSortedPluginHooks @@ -528,21 +522,62 @@ class EnvironmentPluginContainer { } } -class PluginContext implements Omit { +class MinimalPluginContext implements RollupMinimalPluginContext { + constructor( + public meta: PluginContextMeta, + public environment: Environment, + ) {} + + debug(rawLog: string | RollupLog | (() => string | RollupLog)): void { + const log = this._normalizeRawLog(rawLog) + const msg = buildErrorMessage(log, [`debug: ${log.message}`], false) + debugPluginContainerContext?.(msg) + } + + info(rawLog: string | RollupLog | (() => string | RollupLog)): void { + const log = this._normalizeRawLog(rawLog) + const msg = buildErrorMessage(log, [`info: ${log.message}`], false) + this.environment.logger.info(msg, { clear: true, timestamp: true }) + } + + warn(rawLog: string | RollupLog | (() => string | RollupLog)): void { + const log = this._normalizeRawLog(rawLog) + const msg = buildErrorMessage( + log, + [colors.yellow(`warning: ${log.message}`)], + false, + ) + this.environment.logger.warn(msg, { clear: true, timestamp: true }) + } + + error(e: string | RollupError): never { + const err = (typeof e === 'string' ? new Error(e) : e) as RollupError + throw err + } + + private _normalizeRawLog( + rawLog: string | RollupLog | (() => string | RollupLog), + ): RollupLog { + const logValue = typeof rawLog === 'function' ? rawLog() : rawLog + return typeof logValue === 'string' ? new Error(logValue) : logValue + } +} + +class PluginContext + extends MinimalPluginContext + implements Omit +{ ssr = false _scan = false _activeId: string | null = null _activeCode: string | null = null _resolveSkips?: Set - meta: RollupPluginContext['meta'] - environment: Environment constructor( public _plugin: Plugin, public _container: EnvironmentPluginContainer, ) { - this.environment = this._container.environment - this.meta = this._container.minimalContext.meta + super(_container.minimalContext.meta, _container.environment) } parse(code: string, opts: any) { @@ -648,39 +683,41 @@ class PluginContext implements Omit { return '' } - warn( - e: string | RollupLog | (() => string | RollupLog), + override debug(log: string | RollupLog | (() => string | RollupLog)): void { + const err = this._formatLog(typeof log === 'function' ? log() : log) + super.debug(err) + } + + override info(log: string | RollupLog | (() => string | RollupLog)): void { + const err = this._formatLog(typeof log === 'function' ? log() : log) + super.info(err) + } + + override warn( + log: string | RollupLog | (() => string | RollupLog), position?: number | { column: number; line: number }, ): void { - const err = this._formatError(typeof e === 'function' ? e() : e, position) - const msg = buildErrorMessage( - err, - [colors.yellow(`warning: ${err.message}`)], - false, + const err = this._formatLog( + typeof log === 'function' ? log() : log, + position, ) - this.environment.logger.warn(msg, { - clear: true, - timestamp: true, - }) + super.warn(err) } - error( + override error( e: string | RollupError, position?: number | { column: number; line: number }, ): never { // error thrown here is caught by the transform middleware and passed on // the the error middleware. - throw this._formatError(e, position) + throw this._formatLog(e, position) } - debug = noop - info = noop - - private _formatError( - e: string | RollupError, - position: number | { column: number; line: number } | undefined, - ): RollupError { - const err = (typeof e === 'string' ? new Error(e) : e) as RollupError + private _formatLog( + e: string | E, + position?: number | { column: number; line: number } | undefined, + ): E { + const err = (typeof e === 'string' ? new Error(e) : e) as E if (err.pluginCode) { return err // The plugin likely called `this.error` }