diff --git a/.changeset/rich-pianos-fly.md b/.changeset/rich-pianos-fly.md new file mode 100644 index 000000000..b29292026 --- /dev/null +++ b/.changeset/rich-pianos-fly.md @@ -0,0 +1,5 @@ +--- +'@sveltejs/vite-plugin-svelte': minor +--- + +New experimental option sendWarningsToBrowser diff --git a/docs/config.md b/docs/config.md index c64bb772b..6a1bda94b 100644 --- a/docs/config.md +++ b/docs/config.md @@ -332,3 +332,33 @@ export default defineConfig({ ] }); ``` + +### sendWarningsToBrowser + +- **Type:** `boolean` +- **Default:** `false` + + Sends a websocket message `svelte:warnings` with the warnings that are passed to `onwarn`. This is only useful if you build a custom browser based integration where you want to display these. + + **Example** + + ```js + import.meta.hot.on('svelte:warnings', (message) => { + // handle warnings message, eg log to console + console.warn(`Warnings for ${message.filename}`, message.warnings); + }); + ``` + + **Message format** + + ```ts + type SvelteWarningsMessage = { + id: string; + filename: string; + normalizedFilename: string; + timestamp: number; + warnings: Warning[]; // allWarnings filtered by warnings where onwarn did not call the default handler + allWarnings: Warning[]; // includes warnings filtered by onwarn and our extra vite plugin svelte warnings + rawWarnings: Warning[]; // raw compiler output + }; + ``` diff --git a/packages/vite-plugin-svelte/src/handle-hot-update.ts b/packages/vite-plugin-svelte/src/handle-hot-update.ts index b23788b7a..c2e0718c1 100644 --- a/packages/vite-plugin-svelte/src/handle-hot-update.ts +++ b/packages/vite-plugin-svelte/src/handle-hot-update.ts @@ -47,7 +47,7 @@ export async function handleHotUpdate( if (!jsUpdated) { // transform won't be called, log warnings here - logCompilerWarnings(compileData.compiled.warnings, options); + logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options); } const result = [...affectedModules].filter(Boolean) as ModuleNode[]; diff --git a/packages/vite-plugin-svelte/src/index.ts b/packages/vite-plugin-svelte/src/index.ts index 09f8eb717..50ac531f6 100644 --- a/packages/vite-plugin-svelte/src/index.ts +++ b/packages/vite-plugin-svelte/src/index.ts @@ -185,7 +185,7 @@ export function svelte(inlineOptions?: Partial): Plugin[] { } catch (e) { throw toRollupError(e, options); } - logCompilerWarnings(compileData.compiled.warnings, options); + logCompilerWarnings(svelteRequest, compileData.compiled.warnings, options); cache.update(compileData); if (compileData.dependencies?.length && options.server) { compileData.dependencies.forEach((d) => { @@ -232,3 +232,5 @@ export { Processed, Warning } from './utils/options'; + +export { SvelteWarningsMessage } from './utils/log'; diff --git a/packages/vite-plugin-svelte/src/utils/log.ts b/packages/vite-plugin-svelte/src/utils/log.ts index 4dbc52720..591ac4fff 100644 --- a/packages/vite-plugin-svelte/src/utils/log.ts +++ b/packages/vite-plugin-svelte/src/utils/log.ts @@ -2,6 +2,7 @@ import { cyan, yellow, red } from 'kleur/colors'; import debug from 'debug'; import { ResolvedOptions, Warning } from './options'; +import { SvelteRequest } from './id'; const levels: string[] = ['debug', 'info', 'warn', 'error', 'silent']; const prefix = 'vite-plugin-svelte'; @@ -99,18 +100,54 @@ export const log = { setLevel }; -export function logCompilerWarnings(warnings: Warning[], options: ResolvedOptions) { +export type SvelteWarningsMessage = { + id: string; + filename: string; + normalizedFilename: string; + timestamp: number; + warnings: Warning[]; // allWarnings filtered by warnings where onwarn did not call the default handler + allWarnings: Warning[]; // includes warnings filtered by onwarn and our extra vite plugin svelte warnings + rawWarnings: Warning[]; // raw compiler output +}; + +export function logCompilerWarnings( + svelteRequest: SvelteRequest, + warnings: Warning[], + options: ResolvedOptions +) { const { emitCss, onwarn, isBuild } = options; - const warn = isBuild ? warnBuild : warnDev; - const notIgnoredWarnings = warnings?.filter((w) => !ignoreCompilerWarning(w, isBuild, emitCss)); - const extraWarnings = buildExtraWarnings(warnings, isBuild); - [...notIgnoredWarnings, ...extraWarnings].forEach((warning) => { + const sendViaWS = !isBuild && options.experimental?.sendWarningsToBrowser; + let warn = isBuild ? warnBuild : warnDev; + const handledByDefaultWarn: Warning[] = []; + const notIgnored = warnings?.filter((w) => !ignoreCompilerWarning(w, isBuild, emitCss)); + const extra = buildExtraWarnings(warnings, isBuild); + const allWarnings = [...notIgnored, ...extra]; + if (sendViaWS) { + warn = (w: Warning) => { + handledByDefaultWarn.push(w); + warn(w); + }; + } + allWarnings.forEach((warning) => { if (onwarn) { onwarn(warning, warn); } else { warn(warning); } }); + if (sendViaWS) { + const message: SvelteWarningsMessage = { + id: svelteRequest.id, + filename: svelteRequest.filename, + normalizedFilename: svelteRequest.normalizedFilename, + timestamp: svelteRequest.timestamp, + warnings: handledByDefaultWarn, // allWarnings filtered by warnings where onwarn did not call the default handler + allWarnings, // includes warnings filtered by onwarn and our extra vite plugin svelte warnings + rawWarnings: warnings // raw compiler output + }; + log.debug(`sending svelte:warnings message for ${svelteRequest.normalizedFilename}`); + options.server?.ws?.send('svelte:warnings', message); + } } function ignoreCompilerWarning( diff --git a/packages/vite-plugin-svelte/src/utils/options.ts b/packages/vite-plugin-svelte/src/utils/options.ts index 368c34209..aff42aabe 100644 --- a/packages/vite-plugin-svelte/src/utils/options.ts +++ b/packages/vite-plugin-svelte/src/utils/options.ts @@ -560,6 +560,12 @@ export interface ExperimentalOptions { * enable svelte inspector */ inspector?: InspectorOptions | boolean; + + /** + * send a websocket message with svelte compiler warnings during dev + * + */ + sendWarningsToBrowser?: boolean; } export interface InspectorOptions { @@ -609,7 +615,7 @@ export interface InspectorOptions { export interface PreResolvedOptions extends Options { // these options are non-nullable after resolve compilerOptions: CompileOptions; - experimental: ExperimentalOptions; + experimental?: ExperimentalOptions; // extra options root: string; isBuild: boolean;