fix(packages): add server-only bundles #1349
Conversation
…a imports Add `__BROWSER__` compile-time flag and server build modes across the package chain so SSR resolvers get server-safe bundles without browser-only library imports (hls.js, dashjs, mux-embed). Each media directory now has `browser.ts` (full impl) and `server.ts` (SSR-safe stub). Package exports use `browser`/`default` conditions to route imports to the correct bundle. Closes #1343 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
✅ Deploy Preview for vjs10-site ready!
To edit notification comments on pull requests, go to your Netlify project configuration. |
📦 Bundle Size Report🎨 @videojs/html — no changesPresets (7)
Media (8)
Players (3)
Skins (17)
UI Components (24)
Sizes are marginal over the root entry point. ⚛️ @videojs/react — no changesPresets (7)
Media (7)
Skins (14)
UI Components (19)
Sizes are marginal over the root entry point. 🧩 @videojs/core
Entries (11)
🏷️ @videojs/element — no changesEntries (2)
📦 @videojs/store — no changesEntries (3)
🔧 @videojs/utils — no changesEntries (10)
📦 @videojs/spf — no changesEntries (3)
ℹ️ How to interpretAll sizes are standalone totals (minified + brotli).
Run |
Validates that packages with `browser` export conditions have matching server build configuration in tsdown — `'server'` build mode, `__BROWSER__` define, and `platform: 'node'`. Also checks that `default` export paths point to `dist/server/`. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Extract shared HLS types (PreloadType, PlaybackType, SourceType, PlaybackTypes, SourceTypes, inferSourceType) into hls/types.ts - Import shared types in both browser.ts and server.ts instead of redeclaring - Replace `export * from './browser'` in native-hls, simple-hls, and custom-media-element server stubs with explicit no-op classes - Add SSR safety tests for NativeHlsMedia, SimpleHlsMedia, and CustomMediaElement server stubs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
All six media subpath exports now share the same browser/default condition shape, so collapse them into a single `./dom/media/*` wildcard export. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Server stubs only need to not crash on import and construction. Strip them down to bare minimum: public fields for settable props, engine = null, destroy calls detach. No 1:1 mirroring of browser API. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
CSS custom properties are irrelevant on the server. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Simplify HLS type re-exports to `export * from './types'` - Replace `typeof window`/`document` checks with `__BROWSER__` in mux-data.ts and dismiss-layer.ts - Add edge-runtime SSR safety tests across core, html, and react packages using @edge-runtime/vm Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Add serverMethodError helper — server stubs throw clear errors when load()/destroy() are called instead of silently no-oping. - Set __DEV__: true for server builds (SSR has no prod mode). - Rewrite edge SSR tests to iterate package.json exports via bare package specifiers instead of importing from dist/. - Fix bundle-size.js to resolve browser.default (prod bundle) over top-level default (server bundle), and add conditions: ['browser'] to esbuild build() calls. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Bare package specifiers (e.g. `import('@videojs/react/video')`) fail
in pnpm workspaces because packages don't have self-referencing
symlinks. Resolve the server export paths from package.json and import
via file:// URLs instead.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ts from edge tests Rename `serverMethodError(className, method)` to generic `serverError(label)` taking a single string like `HlsMedia.destroy()`. Remove html and react edge SSR tests — they only tested dist imports which src tests should never do. Core keeps its source-based server stub tests in edge-runtime. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add a post-build script (.github/scripts/ssr-check.js) that auto-discovers
all package exports with browser/default condition splits, resolves the
server (default) path, and dynamically imports each one in Node.js. Any
module that throws during evaluation fails the check.
- Add `pnpm check:ssr` root script and CI step in the build job
- Fix ReactiveElement to extend `globalThis.HTMLElement ?? class {}` so
the class definition evaluates safely on the server
- Fix html stubDefinePlugin to emit stub classes (not just `export {}`)
so preset modules can re-export skin element names
- Remove per-package edge-runtime vitest tests (superseded by script)
- 99/99 server entry points now import successfully
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit b5d9b03. Configure here.
| if (typeof exportValue === 'object' && exportValue !== null) { | ||
| if (typeof exportValue.browser === 'object' && exportValue.browser !== null) { | ||
| return exportValue.browser.default ?? exportValue.default ?? null; | ||
| } |
There was a problem hiding this comment.
Bundle size measures server bundle for string browser exports
Medium Severity
resolveDefault only handles browser when it's a nested object (e.g. { browser: { default: "..." } }), but @videojs/utils's ./dom export uses a plain string: { browser: "./dist/dom.js", default: "./dist/server/dom.js" }. Since typeof exportValue.browser === 'object' is false for strings, it falls through to exportValue.default, returning the server bundle path. This causes the bundle-size script to measure the server (no-op) bundle instead of the actual browser bundle for @videojs/utils/dom.
Reviewed by Cursor Bugbot for commit b5d9b03. Configure here.


closes #1351 #1343
Summary
platform: 'node',__BROWSER__: 'false') forcore,html,react,utilsbrowser/defaultexport conditions so SSR resolvers get server-safe bundles__BROWSER__compile-time flag replaces runtimetypeof window/globalThis.documentchecksbrowser.ts+server.tsper media directory — server stubs are no-op classesReactiveElementextendsglobalThis.HTMLElement ?? class {}for SSR-safe module evaluationstubDefinePluginemits stub classes so preset re-exports resolve on serverpnpm check:ssrscript validates all 99 server entry points import cleanly in Node.jsPackages changed
@videojs/corebrowser.ts/server.tsper media dir, 3 build modes,browser/defaultexports, glob-based media entry discovery@videojs/htmlbrowser/defaultexports,safeDefineuses__BROWSER__@videojs/reactbrowser/defaultexports@videojs/utilsbrowsercondition on./dom,__BROWSER__guards@videojs/elementReactiveElementSSR-safe base class fallbackpnpm check:ssradded to build job inci.ymlTest plan
pnpm build:packages— all packages build withdist/server/outputpnpm typecheck— passespnpm test— all 22 test tasks passpnpm check:workspace— 7/7pnpm check:ssr— 99/99 server entry points import successfully🤖 Generated with Claude Code
Note
Medium Risk
Touches build pipelines and
exportsresolution across multiple published packages; misconfiguration could break consumer bundling/SSR imports despite minimal runtime logic changes.Overview
Adds a server build mode (
platform: 'node',__BROWSER__define,dist/server/outputs) across key packages and updates packageexportsto usebrowser/defaultcondition splits so SSR resolvers pick server-safe entry points.Introduces SSR-focused tooling and guardrails: a new
pnpm check:ssrscript (.github/scripts/ssr-check.js) that imports all server entries and is enforced in CI, plus acheck:workspacerule that validatesbrowserexports matchtsdownserver-build configuration.Refactors DOM-only code paths to be compile-time gated via
__BROWSER__(replacing runtime global checks), adds per-media-modulebrowser.ts/server.tsstubs in@videojs/core, and adjusts HTML/React build configs (including HTML stubbing plugins) and tests to target browser entry points.Reviewed by Cursor Bugbot for commit b5d9b03. Bugbot is set up for automated code reviews on this repo. Configure here.