Skip to content

Commit

Permalink
feat: support log related functions in dev
Browse files Browse the repository at this point in the history
  • Loading branch information
sapphi-red committed Dec 9, 2024
1 parent ef7a6a3 commit 74381cc
Show file tree
Hide file tree
Showing 3 changed files with 189 additions and 41 deletions.
1 change: 1 addition & 0 deletions packages/vite/rollup.dts.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ const identifierReplacements: Record<string, Record<string, string>> = {
rollup: {
Plugin$1: 'rollup.Plugin',
PluginContext$1: 'rollup.PluginContext',
MinimalPluginContext$1: 'rollup.MinimalPluginContext',
TransformPluginContext$1: 'rollup.TransformPluginContext',
TransformResult$2: 'rollup.TransformResult',
},
Expand Down
112 changes: 111 additions & 1 deletion packages/vite/src/node/server/__tests__/pluginContainer.spec.ts
Original file line number Diff line number Diff line change
@@ -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', () => {
Expand Down Expand Up @@ -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'
Expand Down Expand Up @@ -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,
}
`)
})
})
})

Expand Down
117 changes: 77 additions & 40 deletions packages/vite/src/node/server/pluginContainer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,17 +40,18 @@ import type {
FunctionPluginHooks,
InputOptions,
LoadResult,
MinimalPluginContext,
ModuleInfo,
ModuleOptions,
NormalizedInputOptions,
OutputOptions,
ParallelPluginHooks,
PartialNull,
PartialResolvedId,
PluginContextMeta,
ResolvedId,
RollupError,
RollupLog,
MinimalPluginContext as RollupMinimalPluginContext,
PluginContext as RollupPluginContext,
TransformPluginContext as RollupTransformPluginContext,
SourceDescription,
Expand Down Expand Up @@ -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({})

Expand All @@ -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'

Expand Down Expand Up @@ -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
Expand Down Expand Up @@ -528,21 +522,62 @@ class EnvironmentPluginContainer {
}
}

class PluginContext implements Omit<RollupPluginContext, 'cache'> {
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<RollupPluginContext, 'cache'>
{
ssr = false
_scan = false
_activeId: string | null = null
_activeCode: string | null = null
_resolveSkips?: Set<Plugin>
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) {
Expand Down Expand Up @@ -648,39 +683,41 @@ class PluginContext implements Omit<RollupPluginContext, 'cache'> {
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 extends RollupLog>(
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`
}
Expand Down

0 comments on commit 74381cc

Please sign in to comment.