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 to reduce consumer bundle size while preserving its root-level public API.

  • Splits public UI components into separate build entries and adds tree-shaking and size-budget checks.
  • Lazily loads non-English locale catalogs while keeping i18next outside the Provider-only graph.
  • Separates Provider chrome, component utilities, and reader typography into independently injected stylesheets.
  • Externalizes shared core runtime code and narrows hook client dependencies.

Confidence Score: 3/5

The PR does not yet appear safe to merge because locale changes can resolve to stale language state and failed locale chunks can emit unhandled rejections.

The two previously reported locale-loading defects remain at current HEAD: asynchronous catalog completion is not guarded by latest-request identity, and every production lazy-load path discards a promise whose rejection is not handled.

Files Needing Attention: packages/ui/src/i18n/index.ts and packages/ui/src/components/YouVersionProvider.tsx

Important Files Changed

Filename Overview
packages/ui/src/i18n/index.ts Introduces lazy locale catalogs, but the previously reported request-ordering and unhandled-rejection failures remain.
packages/ui/src/components/YouVersionProvider.tsx Records host locale without importing i18next and limits Provider style injection to chrome.
packages/ui/tsup.config.ts Splits public component entries, externalizes core, embeds separate style payloads, and enables tree shaking.
packages/ui/package.json Updates the published package surface and dependencies for the split distribution.
scripts/check-tree-shaking.mjs Adds consumer-shaped checks for Provider and component tree-shaking boundaries.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart LR
  App[Consumer import] --> Root[UI package root]
  Root --> Provider[YouVersionProvider entry]
  Root --> Components[Component entries]
  Provider --> Chrome[Chrome styles]
  Components --> Utilities[Component utility styles]
  Components --> I18n[i18next runtime]
  I18n --> English[English catalog]
  I18n --> Lazy[Lazy non-English catalogs]
  Components --> Reader[BibleTextView]
  Reader --> ReaderCSS[Reader typography]
Loading

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

@changeset-bot

changeset-bot Bot commented Sep 3, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 010aedd

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.
@cameronapak
cameronapak force-pushed the cp/test-small-pkg-size branch from e82174e to 0803452 Compare September 9, 2026 16:50
cameronapak added a commit that referenced this pull request Sep 9, 2026
…d bytes (#384)

* perf(core): split clients into tree-shakable modules and cut published bytes

Stack 1/3 of #382. Core-only changes: module split into bible-*/languages-*
files, zod-mini schemas, minified browser CSS, shared filtered paged-list
helper, and the version-stamp check update.

* chore(changeset): add core changeset for the tree-shakable module split

* fix(core): restore expressive palette, chart, and sidebar theme tokens

* refactor(core): move language and version options schemas into schemas/

* fix(core): centralize input schemas in schemas/ and restore page_size refine

- Move client-module input schemas (version/book/chapter/verse ids, language
  id, organization id, highlight passage/color) into schemas/ so schemas stay
  the single source of truth; import schema files directly, not the barrel,
  to keep tree-shakable entries narrow
- Restore the page_size="*" 1-3 fields refine on GetLanguagesOptionsSchema
  (dropped in the module split, replaced by an unreachable plain-Error check)
- Dedupe BCP-47 regex and hex-color schema; type fetchFilteredCollection params
- Document zod-mini $ZodError behavior note in the changeset; refresh core
  AGENTS.md STRUCTURE

* fix(core): restore ui stamp, page_size Error, css watch, and ZodError identity

Keep the changeset as patch. Restore the UI version-stamp entry so UI
prepublishOnly still works, throw the prior page_size="*" Error message,
watch core CSS into dist/styles during pnpm dev, and wrap Mini $ZodError
at the public client boundary so instanceof ZodError still matches.

* fix(core): drop ZodError wrap and document Mini $ZodError as patch

Keep zod Mini as the public validation identity. Remove parsePublic and
restore direct schema.parse(). Changeset stays patch and notes that
schema failures throw $ZodError, not classic ZodError. page_size="*",
UI stamp, and CSS watch stay as-is.

---------

Co-authored-by: Cursor Agent <cursoragent@cursor.com>
… (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 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.
@cameronapak
cameronapak force-pushed the cp/test-small-pkg-size branch from 0803452 to 010aedd Compare September 9, 2026 18:59
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