Skip to content

perf: cut published package and partner-bundle bytes (YPE-5528) - #382

Open
cameronapak wants to merge 2 commits into
cp/hooks-splitfrom
cp/test-small-pkg-size
Open

perf: cut published package and partner-bundle bytes (YPE-5528)#382
cameronapak wants to merge 2 commits into
cp/hooks-splitfrom
cp/test-small-pkg-size

Conversation

@cameronapak

@cameronapak cameronapak commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Summary

  • Split the UI dist so a named import from the package root can drop unused Bible UI. No new public subpaths.
  • Keep i18next off the Provider graph, slim unused locale keys, and stop inlining a second copy of core into UI.
  • Pull BibleClient / LanguagesClient off narrow hook graphs. Host client overrides still win.
  • Provider injects chrome only. Components that need utilities or reader typography inject those sheets themselves. The public stylesheet is utilities plus reader.

YPE-5528. Parent YPE-1193.

Size saved per import path

Minify + brotli, esbuild, no splitting. React, react-dom, jsx-runtime, react-query, and jsdom are external. Same ruler as docs/ui-import-size-research.md.

On main (5ddc2e0), any one named UI import measured like Provider: 196.1 kB. The full barrel was 203.6 kB. The gap was 42 kB raw.

Import from @youversion/platform-react-ui main this PR saved
{ YouVersionProvider } 196.1 kB 16.2 kB 180 kB (92%)
{ BibleReader } 196.1 kB 144.3 kB 52 kB (26%)
{ BibleCard } 196.1 kB 108.2 kB 88 kB (45%)
{ Separator } 196.1 kB 15.6 kB 180 kB (92%)
import * full barrel 203.6 kB 159.3 kB 44 kB (22%)

Named import from the root matches the component entry file. Do not add ./bible-reader.

pnpm size on this branch (same no-split gate):

Path now budget main budget
core full 19.38 kB 21 64
{ ApiClient } 6.45 kB 8 59
hooks full 23.12 kB 26 68
{ useChapter } 9 kB 10 64
{ YouVersionProvider } 16.07 kB 18 (new row)
UI full barrel 158.42 kB 175 235

BibleReader stays large because of i18next, the full utility sheet, and passage/jsdom. Replacing i18next was considered and skipped. Provider already avoids that runtime.

Test plan

  • CI=true pnpm size stays under the new budgets
  • pnpm check:tree-shaking (Provider absent Bible reader/picker sentinels; useChapter absent BibleClient / jsdom)
  • examples/vite-react: Provider-only page, then BibleReader + pickers, then Sign in
  • CJS require('@youversion/platform-react-ui') still resolves
  • locale on Provider still translates missing-app-key copy without loading i18next
  • Public styles.css still includes reader typography
  • Storybook integration plays pass after a Provider-only chrome inject

Greptile Summary

The PR restructures the UI package for smaller consumer bundles while preserving its root public API.

  • Splits public UI components into independently tree-shakeable build entries.
  • Moves non-English catalogs to lazy loading and keeps i18next off the provider-only graph.
  • Separates chrome, component, and reader style injection.
  • Externalizes core from UI and narrows client dependencies in hook graphs.

Confidence Score: 3/5

The PR is not yet safe to merge because locale requests can still resolve out of order and failed locale chunks can still produce unhandled promise rejections.

The current lazy-loading path allows an older locale import to apply after a newer request, while dynamic-import failures propagate into multiple fire-and-forget callers without rejection handling.

Files Needing Attention: packages/ui/src/i18n/index.ts

Important Files Changed

Filename Overview
packages/ui/src/i18n/index.ts Introduces lazy locale loading, but the two previously reported ordering and rejection-handling failures remain.
packages/ui/src/i18n/pending-locale.ts Adds lightweight provider-to-i18n locale state and subscriptions without importing the full translation runtime.
packages/ui/src/components/YouVersionProvider.tsx Removes i18next and full component styles from the provider graph while forwarding locale through pending state.
packages/ui/tsup.config.ts Splits public UI modules into build entries and separates embedded stylesheet payloads.
packages/ui/src/index.ts Reworks root exports to retain the public API while allowing named imports to tree-shake.
scripts/check-tree-shaking.mjs Adds consumer-shaped checks for provider and component bundle boundaries.

Sequence Diagram

sequenceDiagram
  participant Host
  participant Provider as YouVersionProvider
  participant Pending as Pending locale state
  participant I18n as i18n module
  participant Chunk as Locale chunk
  Host->>Provider: Render with locale
  Provider->>Pending: requestSdkLanguage(locale)
  Pending-->>I18n: Notify translating component graph
  I18n->>Chunk: Dynamically import locale catalog
  Chunk-->>I18n: Catalog module
  I18n->>I18n: Add resources and change language
Loading

Reviews (2): Last reviewed commit: "chore(changeset): scope the UI split cha..." | Re-trigger Greptile

Context used:

@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 0803452

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 4 packages
Name Type
@youversion/platform-react-ui Patch
vite-react Patch
@youversion/platform-core Patch
@youversion/platform-react-hooks Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

Comment on lines +69 to +74
return ensureLocale(detected).then(() => {
if (i18n.language === detected) {
return detected;
}
return i18n.changeLanguage(detected).then(() => detected);
});

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Stale locale requests win

If two non-English locale requests overlap, each continuation calls changeLanguage after its own import resolves without checking which locale was requested most recently. A slower older request can therefore overwrite the newer locale and leave SDK copy displayed in the wrong language.

Knowledge Base Used: React UI component library

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/i18n/index.ts
Line: 69-74

Comment:
**Stale locale requests win**

If two non-English locale requests overlap, each continuation calls `changeLanguage` after its own import resolves without checking which locale was requested most recently. A slower older request can therefore overwrite the newer locale and leave SDK copy displayed in the wrong language.

**Knowledge Base Used:** [React UI component library](https://app.greptile.com/youversion/-/custom-context/knowledge-base/youversion/platform-sdk-react/-/docs/react-ui.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Cursor Fix in Codex

Comment on lines +105 to +110
subscribeSdkLanguage((languageTag) => {
void syncSdkLanguage(languageTag);
});
const pendingLocale = getRequestedSdkLanguage();
if (pendingLocale.requested) {
void syncSdkLanguage(pendingLocale.languageTag);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Locale load rejections escape

If a non-English locale chunk cannot be fetched, ensureLocale rejects and these production call sites discard the promise without a rejection handler. This emits an unhandled promise rejection while leaving the requested language inactive, and runtimes configured to treat unhandled rejections as fatal can terminate the affected execution context.

Knowledge Base Used: React UI component library

Prompt To Fix With AI
This is a comment left during a code review.
Path: packages/ui/src/i18n/index.ts
Line: 105-110

Comment:
**Locale load rejections escape**

If a non-English locale chunk cannot be fetched, `ensureLocale` rejects and these production call sites discard the promise without a rejection handler. This emits an unhandled promise rejection while leaving the requested language inactive, and runtimes configured to treat unhandled rejections as fatal can terminate the affected execution context.

**Knowledge Base Used:** [React UI component library](https://app.greptile.com/youversion/-/custom-context/knowledge-base/youversion/platform-sdk-react/-/docs/react-ui.md)

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Fix in Claude Code Fix in Cursor Fix in Codex

cameronapak added a commit that referenced this pull request Sep 9, 2026
Stack 2/3 of #382. Hooks-only changes: deep imports of YouVersionContext,
client-override fallback shape per hook, and query-key changes.
… (YPE-5528)

Stack 3/3 — UI dist split, i18n lazy locales, Storybook sheet loading, and
the CI gates (tree-shaking UI row, size budgets, spec sentinels).
@cameronapak
cameronapak force-pushed the cp/test-small-pkg-size branch from e82174e to 0803452 Compare September 9, 2026 16:50
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant