From e8ad92592d5606896751f8d4da45b5114c1da590 Mon Sep 17 00:00:00 2001 From: Pascal Date: Tue, 7 Apr 2026 10:17:00 -0400 Subject: [PATCH] fix(viewer): suppress THREE.Clock deprecation warning from R3F 9.x (#215) three.js r183 deprecated THREE.Clock, but @react-three/fiber 9.x still uses it internally per mount. This floods the dev console with noise on every page load and HMR reload. Uses three's setConsoleFunction hook to surgically suppress only the exact Clock deprecation message. All other three.js logs pass through untouched, including TSL stack-trace warnings. Safe to remove once R3F v10 stable ships (which replaces Clock with Timer). Removal condition documented in file header. Closes #213 --- .../viewer/src/components/viewer/index.tsx | 4 ++ .../src/lib/suppress-three-clock-warning.ts | 63 +++++++++++++++++++ 2 files changed, 67 insertions(+) create mode 100644 packages/viewer/src/lib/suppress-three-clock-warning.ts diff --git a/packages/viewer/src/components/viewer/index.tsx b/packages/viewer/src/components/viewer/index.tsx index ada0067e..eca2c561 100644 --- a/packages/viewer/src/components/viewer/index.tsx +++ b/packages/viewer/src/components/viewer/index.tsx @@ -1,5 +1,9 @@ 'use client' +// Must run before @react-three/fiber's Canvas instantiates new THREE.Clock(). +// See lib/suppress-three-clock-warning.ts for rationale and removal condition. +import '../../lib/suppress-three-clock-warning' + import { CeilingSystem, DoorSystem, diff --git a/packages/viewer/src/lib/suppress-three-clock-warning.ts b/packages/viewer/src/lib/suppress-three-clock-warning.ts new file mode 100644 index 00000000..d1892f2e --- /dev/null +++ b/packages/viewer/src/lib/suppress-three-clock-warning.ts @@ -0,0 +1,63 @@ +/** + * Suppresses the `THREE.Clock: This module has been deprecated` warning + * emitted by three.js r183+ on every `new THREE.Clock()` call. + * + * We don't instantiate `Clock` ourselves — `@react-three/fiber` 9.x + * (current stable) creates one internally per `` mount. The + * migration to `THREE.Timer` lands in R3F 10.x (still alpha). + * + * Uses three's `setConsoleFunction` hook so we don't touch `console.warn` + * globally. Only the exact Clock deprecation message is suppressed; all + * other three.js logs (including TSL stack-trace warnings) pass through + * untouched. + * + * Runs in production as well as dev — the suppressed message is a pure + * deprecation notice with no user-actionable content. + * + * REMOVAL: safe to delete once `@react-three/fiber` no longer constructs + * `new THREE.Clock()`. To verify after an R3F upgrade: + * grep -n "new THREE.Clock" node_modules/@react-three/fiber/dist/*.js + * No hits → delete this file and its import in components/viewer/index.tsx. + * + * @see https://github.com/pascalorg/editor/issues/213 + */ + +import { setConsoleFunction } from 'three' + +// three's warn() prepends 'THREE.' to its first argument, and the Clock +// constructor passes 'THREE.Clock: ...', producing a double-prefixed +// string. Exact equality keeps the suppression surgical — if three ever +// rewords this, the filter stops matching and the message resurfaces. +const CLOCK_DEPRECATION_MESSAGE = + 'THREE.THREE.Clock: This module has been deprecated. Please use THREE.Timer instead.' + +type ConsoleMethod = 'log' | 'warn' | 'error' + +// HMR-idempotent install guard. setConsoleFunction is a single-slot global +// in three, so re-evaluating this module on HMR would reinstall the hook. +const INSTALLED = Symbol.for('@pascal-app/viewer/suppress-three-clock-warning') +type GlobalWithFlag = typeof globalThis & { [INSTALLED]?: true } + +if (!(globalThis as GlobalWithFlag)[INSTALLED]) { + ;(globalThis as GlobalWithFlag)[INSTALLED] = true + + setConsoleFunction((method: ConsoleMethod, message: string, ...params: unknown[]) => { + if (method === 'warn' && message === CLOCK_DEPRECATION_MESSAGE) { + return + } + + // Mirror three's default stack-trace handling so TSL warnings/errors + // keep their clickable stack frames. + if (method !== 'log') { + const first = params[0] as + | { isStackTrace?: boolean; getError?: (m: string) => Error } + | undefined + if (first?.isStackTrace && typeof first.getError === 'function') { + console[method](first.getError(message)) + return + } + } + + console[method](message, ...params) + }) +}