diff --git a/packages/compiler/package.json b/packages/compiler/package.json index 62d94999..cc78ba7c 100644 --- a/packages/compiler/package.json +++ b/packages/compiler/package.json @@ -64,6 +64,14 @@ "test:watch": "vitest", "clean": "rimraf --glob dist types '*.tsbuildinfo'" }, + "peerDependencies": { + "vite": "^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "vite": { + "optional": true + } + }, "dependencies": { "@videojs/utils": "workspace:*", "kleur": "^4.1.5", @@ -74,6 +82,7 @@ "devDependencies": { "@videojs/core": "workspace:*", "tsdown": "^0.21.4", + "vite": "^8.0.0", "vitest": "^4.1.0" } } diff --git a/packages/compiler/src/plugins/tests/vite.test.ts b/packages/compiler/src/plugins/tests/vite.test.ts index b5cc1129..bc2b4264 100644 --- a/packages/compiler/src/plugins/tests/vite.test.ts +++ b/packages/compiler/src/plugins/tests/vite.test.ts @@ -2,6 +2,19 @@ import { describe, expect, it, vi } from 'vitest'; import type { CompilerConfig } from '../../config'; import { vjsCompiler } from '../vite'; +type TestPlugin = { + resolveId(id: string): string | null; + load(id: string): string | null; + transform( + this: { warn(warning: unknown): void }, + code: string, + id: string + ): Promise<{ code: string; map: null } | null>; +}; + +const createPlugin = (...args: Parameters): TestPlugin => + vjsCompiler(...args) as unknown as TestPlugin; + const createCssStyle = (source: string): NonNullable => ({ name: 'fixture', setup(context) { @@ -16,10 +29,9 @@ const createCssStyle = (source: string): NonNullable = describe('vjsCompiler', () => { it('imports emitted CSS assets as virtual modules', async () => { - const plugin = vjsCompiler({ config: { styles: createCssStyle('.foo{display:flex;}') } }); - plugin.configResolved?.({ root: '/workspace' }); + const plugin = createPlugin({ config: { styles: createCssStyle('.foo{display:flex;}') } }); - const result = await plugin.transform!.call( + const result = await plugin.transform.call( { warn: () => {} }, `function App(){ return ; }`, '/workspace/skin.tsx' @@ -31,14 +43,14 @@ describe('vjsCompiler', () => { const id = match![1]!; expect(id).toContain('virtual:@videojs/compiler/css/'); - expect(plugin.resolveId?.(id)).toBe(`\0${id}`); - expect(plugin.load?.(`\0${id}`)).toBe('.foo{display:flex;}'); + expect(plugin.resolveId(id)).toBe(`\0${id}`); + expect(plugin.load(`\0${id}`)).toBe('.foo{display:flex;}'); expect(result!.code).toContain('function App'); }); it('forwards compiler warnings to Vite', async () => { const warn = vi.fn(); - const plugin = vjsCompiler({ + const plugin = createPlugin({ config: { styles: { name: 'fixture', @@ -50,14 +62,14 @@ describe('vjsCompiler', () => { }, }); - await plugin.transform!.call({ warn }, `function App(){ return ; }`, '/workspace/skin.tsx'); + await plugin.transform.call({ warn }, `function App(){ return ; }`, '/workspace/skin.tsx'); expect(warn).toHaveBeenCalledWith('Check this'); }); it('forwards located compiler warnings to Vite', async () => { const warn = vi.fn(); - const plugin = vjsCompiler({ + const plugin = createPlugin({ config: { styles: { name: 'fixture', @@ -77,7 +89,7 @@ describe('vjsCompiler', () => { }, }); - await plugin.transform!.call({ warn }, `function App(){ return ; }`, '/workspace/skin.tsx'); + await plugin.transform.call({ warn }, `function App(){ return ; }`, '/workspace/skin.tsx'); expect(warn).toHaveBeenCalledWith({ message: 'Check this location', diff --git a/packages/compiler/src/plugins/vite.ts b/packages/compiler/src/plugins/vite.ts index 37f8582b..3b1f9c12 100644 --- a/packages/compiler/src/plugins/vite.ts +++ b/packages/compiler/src/plugins/vite.ts @@ -1,3 +1,4 @@ +import type { Plugin } from 'vite'; import { compile } from '../compile'; import type { CompilerConfig, CompilerDiagnostic } from '../config'; import { type LoadedCompilerConfig, loadConfig } from '../load-config'; @@ -9,27 +10,18 @@ export interface VideojsCompilerPluginOptions { exclude?: readonly string[] | undefined; } -export interface VitePluginContext { - warn(warning: string | VitePluginWarning): void; -} +type ViteHookContext = Hook extends (this: infer Context, ...args: never[]) => unknown + ? Context + : Hook extends { handler: (this: infer Context, ...args: never[]) => unknown } + ? Context + : never; -export interface VitePluginWarning { - message: string; - id?: string | undefined; - loc?: { file?: string | undefined; line: number; column?: number | undefined } | undefined; - pluginCode?: string | undefined; -} +type ViteTransformContext = ViteHookContext>; +type VitePluginWarning = ViteTransformContext extends { warn: (...args: infer WarningParameters) => unknown } + ? WarningParameters[0] + : string; -export interface VitePlugin { - name: string; - enforce?: 'pre' | 'post'; - configResolved?: (config: { root: string }) => void; - resolveId?: (id: string) => string | null; - load?: (id: string) => string | null; - transform?: (this: VitePluginContext, code: string, id: string) => Promise<{ code: string; map: null } | null>; -} - -export function vjsCompiler(options: VideojsCompilerPluginOptions = {}): VitePlugin { +export function vjsCompiler(options: VideojsCompilerPluginOptions = {}): Plugin { const include = options.include ?? ['.tsx']; const exclude = options.exclude ?? []; const cssById = new Map(); @@ -84,7 +76,7 @@ function cssVirtualId(id: string, fileName: string, index: number): string { return `virtual:@videojs/compiler/css/${encodeURIComponent(id)}/${index}/${encodeURIComponent(fileName)}`; } -function viteWarningFromDiagnostic(diagnostic: CompilerDiagnostic): string | VitePluginWarning { +function viteWarningFromDiagnostic(diagnostic: CompilerDiagnostic): VitePluginWarning { if (!diagnostic.file || !diagnostic.line) return diagnostic.message; return { message: diagnostic.message, @@ -92,7 +84,7 @@ function viteWarningFromDiagnostic(diagnostic: CompilerDiagnostic): string | Vit loc: { file: diagnostic.file, line: diagnostic.line, - ...(diagnostic.column ? { column: diagnostic.column } : {}), + column: diagnostic.column ?? 0, }, pluginCode: diagnostic.code, }; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 29f95937..7d03290a 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -245,6 +245,9 @@ importers: tsdown: specifier: ^0.21.4 version: 0.21.9(@typescript/native-preview@7.0.0-dev.20260421.1)(typescript@6.0.2) + vite: + specifier: ^8.0.0 + version: 8.0.0(@types/node@24.12.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2) vitest: specifier: ^4.1.0 version: 4.1.0(@opentelemetry/api@1.9.1)(@types/node@24.12.2)(@vitest/browser-playwright@4.1.0)(@vitest/ui@4.1.0)(happy-dom@18.0.1)(jsdom@27.4.0)(vite@8.0.0(@types/node@24.12.2)(esbuild@0.27.3)(jiti@2.6.1)(tsx@4.21.0)(yaml@2.8.2))