You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Since v2.20.0, constructing any GraphicsDevice (including NullGraphicsDevice) throws if the supplied canvas does not implement getBoundingClientRect():
TypeError: this.canvas.getBoundingClientRect is not a function
at NullGraphicsDevice.updateClientRect
at new GraphicsDevice
at new NullGraphicsDevice
#8950 (first released in v2.20.0) moved the updateClientRect() call into the base GraphicsDevice constructor so that clientRect is valid before the first frame (fixing #8932). updateClientRect() calls this.canvas.getBoundingClientRect() unconditionally, which only exists on real DOM elements.
NullGraphicsDevice is the documented path for headless / server-side use, where consumers typically pass a mock canvas object. For example, @playcanvas/react passes { id: 'pc-react-mock-canvas' } to NullGraphicsDevice for SSR — and does so at module scope, so with engine ≥ 2.20.0 merely importing @playcanvas/react in Node crashes.
Impact
Broke the developer.playcanvas.com Docusaurus build: static site generation failed for all 639 doc pages once the site upgraded to playcanvas 2.20.4. The site is temporarily pinned to ~2.19.7 (Pin playcanvas to ~2.19.7 to fix broken build developer-site#1074) until this is fixed.
Any headless/SSR consumer constructing a NullGraphicsDevice with a non-DOM canvas is affected.
Steps to Reproduce
In Node (no DOM):
import{NullGraphicsDevice}from'playcanvas';constmockCanvas={id: 'mock'};newNullGraphicsDevice(mockCanvas);// throws in >= 2.20.0, works in 2.19.7
Suggested fix
Guard the constructor-time initialization so it degrades gracefully when the canvas lacks getBoundingClientRect (e.g. typeof this.canvas.getBoundingClientRect === 'function' in updateClientRect(), or skip/override for the null backend), while preserving the intent of #8950 — a valid clientRect before the first device update for real backends.
There is a related hardening issue on the consumer side: playcanvas/react's SSR mock canvas could also stub this method (issue to follow on that repo).
Description
Since v2.20.0, constructing any
GraphicsDevice(includingNullGraphicsDevice) throws if the supplied canvas does not implementgetBoundingClientRect():#8950 (first released in v2.20.0) moved the
updateClientRect()call into the baseGraphicsDeviceconstructor so thatclientRectis valid before the first frame (fixing #8932).updateClientRect()callsthis.canvas.getBoundingClientRect()unconditionally, which only exists on real DOM elements.NullGraphicsDeviceis the documented path for headless / server-side use, where consumers typically pass a mock canvas object. For example,@playcanvas/reactpasses{ id: 'pc-react-mock-canvas' }toNullGraphicsDevicefor SSR — and does so at module scope, so with engine ≥ 2.20.0 merely importing@playcanvas/reactin Node crashes.Impact
~2.19.7(Pin playcanvas to ~2.19.7 to fix broken build developer-site#1074) until this is fixed.NullGraphicsDevicewith a non-DOM canvas is affected.Steps to Reproduce
In Node (no DOM):
Suggested fix
Guard the constructor-time initialization so it degrades gracefully when the canvas lacks
getBoundingClientRect(e.g.typeof this.canvas.getBoundingClientRect === 'function'inupdateClientRect(), or skip/override for the null backend), while preserving the intent of #8950 — a validclientRectbefore the first device update for real backends.There is a related hardening issue on the consumer side: playcanvas/react's SSR mock canvas could also stub this method (issue to follow on that repo).
🤖 Generated with Claude Code