Repository navigation
fix(embed): the Starlight, Astro and VitePress plugins need no React installed - #167
Merged
Merged
Conversation
…installed The plugins mounted the launcher from ondocs/embed, whose dialog chunk imports react, react-dom, @radix-ui/react-dialog and cmdk, so a Vue or Astro site had to install all four or its build failed. A new build, ondocs/embed/standalone, is ondocs/embed with production React, Radix and cmdk bundled into the dialog's chunk. launcherScript mounts with it, so the guides now install only ondocs, ai and @ai-sdk/openai. js-weight.mjs gets an on-open budget for each site, and the fresh Starlight and VitePress sites in the consumers job install no React.
…m source The tests run before the package is built, so the fixture aliases ondocs's modules to their source; the launcher now imports ondocs/embed/standalone.
…d starlight-ondocs ondocs init still printed react, react-dom, @radix-ui/react-dialog and cmdk for Starlight, Astro and VitePress, the landing page's install step showed them, and starlight-ondocs asked for them as required peers, which npm installs. The plugins mount ondocs/embed/standalone now, so none of them is needed: init lists ondocs, ai and @ai-sdk/openai, and starlight-ondocs asks only for Starlight, Astro and the AI SDK. The docs said openAskDialog from ondocs/embed works in any framework, but on a site without React that import fails the build. ask-dialog.md now says such a site imports it from ondocs/embed/standalone or uses data-ondocs-open, api.md lists the new module and says ondocs/embed needs the site's React, and writing-an-integration.md says what launcherScript mounts. The astro-* deploy recipes and the starlight-ondocs consumer site install no React and check it stays out. The changeset is minor, as the release adds a module.
…nteraction Starlight imports its search UI, about 25 KB gzipped, in a requestIdleCallback. On a busy CI runner that callback ran after the network had gone quiet and the 500 ms wait had passed, so the script charged it to the dialog's opening: Starlight measured 10.3 KB before interaction and +118.4 KB on open, over the 97 KB budget, against 36.1 KB and +92.6 KB on every other run. The script now waits for an idle callback queued after the page's own, then until every script requested has loaded and none has been requested for 500 ms. With the page's idle callbacks delayed by 1.5 s, the old script measured the Starlight example at 10.4 KB before interaction, and this one 36.2 KB. The figures of the unmodified builds are unchanged.
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Roadmap item: OD2-50, a dialog without React for non-React hosts.
What was wrong
The Starlight, Astro and VitePress plugins mounted the launcher from
ondocs/embed. Its dialog chunk importsreact,react-dom,@radix-ui/react-dialogandcmdk, so a Vue or Astro site had to install all four or its build failed. The guides said so: README, astro.md and vitepress.md all hadnpm i ondocs ai @ai-sdk/openai react react-dom @radix-ui/react-dialog cmdk. The site's bundler then packed React into the dialog chunk anyway.What changed
ondocs/embed/standalone(tsdown.config.ts,package.jsonexports). It issrc/embed/index.tsbuilt a second time, with production React, Radix and cmdk bundled into the dialog's chunk. The types areondocs/embed's. The entry (dist/embed/standalone.js, 1,773 B gzip -9) imports only its dialog chunk, and the chunk (95,486 B gzip -9) imports nothing. The site's bundler still splits the dialog off and loads it on first use.launcherScriptnow mounts fromondocs/embed/standalone, and so do the Astro/Starlight integration and the VitePress plugin.optimizeDeps.includefollows the new name. They no longer callwarnMissingDialogPeers, which would now warn about packages nobody needs. Docusaurus is unchanged and still uses the site's React.npm i ondocs ai @ai-sdk/openai, in the README, astro.md, vitepress.md and the setup skill (test/setup-skill.test.tskeeps the skill equal to the guides). Each guide notes that 0.11.0 and earlier still need the four packages. The guides give the measured size on open and say the dialog's stylesheet (3.5 KB gz) is added to the site's CSS on every page; that covers the od-eager-dialog-css finding with wording. script-tag.md's "With a bundler" section documents both modules.scripts/js-weight.mjs: each site now has an on-open budget,open, which--checkenforces. VitePress was already measured. It also waits for the page's idle callbacks before it stops counting what the page loads before interaction. Starlight imports its search UI (about 25 KB gzipped) in arequestIdleCallback. On one CI run of this branch that callback ran late, so the script charged it to the dialog: 10.3 KB before interaction and +118.4 KB on open, over the 97 KB budget. With the page's idle callbacks delayed by 1.5 s, the old script measured 10.4 KB and the new one 36.2 KB. The unmodified builds measure the same as before.scripts/consumer-site.mjs: the fresh Starlight, starlight-ondocs and VitePress sites install onlyaiand@ai-sdk/openai, and fail if react, react-dom, Radix or cmdk end up installed.ondocs init(sitePackages) no longer listsreact react-dom @radix-ui/react-dialog cmdkfor Starlight, Astro and VitePress. It printsnpm i ondocs ai @ai-sdk/openai. Four init snapshots changed.starlight-ondocsno longer asks for react, react-dom,@radix-ui/react-dialogandcmdkas peers. npm installs required peers, so before this every starlight-ondocs site got React. Its README install line isnpm i starlight-ondocs ondocs ai @ai-sdk/openai.pnpm-lock.yamlloses those four entries from thepackages/starlight-ondocsimporter (12 lines removed, nothing else changed).npm i ondocs ai @ai-sdk/openai.openAskDialogon a site without React.ondocs/embedrenders the dialog with the site's React, so a page on such a site that importsopenAskDialogfrom it fails the build. ask-dialog.md now says that a site without React (Astro, Starlight, VitePress) imports it fromondocs/embed/standalone, or usesdata-ondocs-open, which needs no import. api.md has a row forondocs/embed/standalone, and itsondocs/embedrow says it needs the site's React,@radix-ui/react-dialogandcmdk.launcherScriptmountsmountAskDialogfromondocs/embed/standalone, with React, Radix and cmdk built into the dialog's chunk, so the site installs none of them.quietUseClient()is described as being for a site that bundlesondocs/reactitself, andwarnMissingDialogPeersas being for a dialog that renders with the site's React.scripts/deploy-recipes.mjs: the astro-* recipes no longer install React, Radix or cmdk, and fail if any of them is installed. CI now builds a plain Astro site (not Starlight) without React.OndocsDialogOptionsin the Astro, Starlight and VitePress entries namesondocs/embed/standalone. benchmarks.md's Starlight on-open figure is +92.6 KB, as measured.ondocsandstarlight-ondocs, as the release adds a module (ondocs/embed/standalone) and changes whatlauncherScriptemits. CONTRIBUTING says minor for a feature.src/embed/dialog-module.tsfrom the planned file list was not needed, because the new build reusessrc/embed/index.tsas its entry. Four files outside the planned list changed because the gate requires it.test/vitepress.test.tsandtest/integration-entry.test.tseach had a one-line assertion of the oldondocs/embedspecifier.test/fixtures/vitepress-site/.vitepress/config.mtsmaps ondocs's modules to their source, because the tests run before the build; it now maps the new specifier too.skills/ondocs-setup/SKILL.mdmust match the guides' install line. The example doc copies were regenerated withpnpm examples:sync.Size, before and after
Measured with
node scripts/js-weight.mjs starlight vitepresson an Apple M1 Max (gzip -9, KB of 1,024 bytes), on main (02fba58) and on this branch, after building each example:On this branch, Docusaurus measures 177.7 KB / +27.2 KB, Next.js 180 KB / +27.3 KB and the script tag 3.2 KB / +97.8 KB. None of those changed. The new on-open budgets are 31, 97, 100, 31 and 102 KB.
preact/compat: tried, not shipped
The finding proposed building this bundle against preact/compat. I built
src/embed/dialog-global.tswith React aliased to preact@10.29.8/compat (already in the lockfile, through VitePress's docsearch). It came out at 41,027 B gzip -9, against 100,044 B for today'sembed-dialog.global.js. I then drove both builds through the same scripted run in headless Chrome. Most of it matched: open and focus, Tab trapped, scroll lock,aria-hiddenon the page, answer and sources, Escape with focus return, overlay click. The dialog's DOM was the same apart from attribute order. Two things differed:spellCheck={false}on the input is dropped, because preact removes attributes set tofalse.Fixing the first needs a change in
dialog-panel.tsx. Running the dialog test suite against preact/compat needs a vitest alias config andpreactas a devDependency, which means a lockfile change while OD2-46 is open. Both are outside this item's files, so this PR ships the React build. The install friction goes away now, and the weight cut is a follow-up.How it was tested
test/standalone-dialog.test.ts(5 tests). It fails on main for the finding's reasons: the install lines name React, the launcher importsondocs/embed, and nothing buildsembed/standalone. The test builds the entry with tsdown fromtsdown.config.tsand checks that the entry imports only its chunk and the chunk imports nothing. It checks that the entry is under 2.5 KB gz and that React sits in the chunk. In jsdom it opens the dialog, asks, gets the citation and closes on Escape.pnpm install --frozen-lockfile --prefer-offline(lockfile unchanged), thenpnpm validate: lint, format:check, typecheck, test (1270 passed, 10 skipped), build, check:package (publint and attw coverondocs/embed/standalone), examples:check, template:check and the four example builds. Alsopnpm check:doc-imports,node scripts/embed-size.mjs, andnode scripts/js-weight.mjs docusaurus starlight vitepress embed --check. Next.js was checked with the same script on a local port of my choice.ondocs/embed/standalonechunk loads only when the dialog opens. A stubbed answer (intercepted in the browser, nothing sent to the live endpoint) shows its citation. There is no sideways scroll (scrollWidth 390 and 1440). Focus returns to the navbar button after Escape, the shortcut reopens the dialog, and the console shows no errors.vitepress devandastro devprebundleondocs_embed_standalone.jsand its chunk, and the dialog answers.astro devreloads once on a cold cache, as it does on main (Astro's dev toolbar deps).consumersCI job builds fresh Starlight and VitePress sites from the packed tarball without React.pnpm install --frozen-lockfile --prefer-offline, thenpnpm validatepassed: lint, format:check, typecheck, test (1283 passed, 10 skipped), build, check:package, examples:check, template:check and the four example builds.node scripts/check-doc-imports.mjsresolves the newopenAskDialogfromondocs/embed/standalone.test/standalone-dialog.test.tsnow also checks starlight-ondocs's README and the landing page's Astro and VitePress install steps. Both checks fail with the old lines.test/init.test.tschecks the Astro-with-adapter install line, andtest/plugin-packages.test.tschecks each plugin package's exact peers.node scripts/consumer-site.mjs starlight-ondocsbuilt a fresh Starlight site from the packed tarballs with no react, react-dom, Radix or cmdk innode_modules.node scripts/deploy-recipes.mjs astro-nodebuilt and served a plain Astro site without them and answered.consumer-site.mjs vitepress) with a page importingopenAskDialog: fromondocs/embed/standalone,vitepress buildpassed. Fromondocs/embed, it failed with"useSyncExternalStore" is not exported by "__vite-optional-peer-dep:react:ondocs", which is what the docs now say.next starton a local port, headless Chrome at 390 and 1440 px: the Astro/Starlight and VitePress Install steps shownpm i ondocs ai @ai-sdk/openai, with no sideways scroll (scrollWidth 390 and 1440). The only console errors are the 404 for Vercel's analytics script, which exists only on Vercel.Trade-offs
npm pack) is 430,008 B on this branch, up from 332,815 B on main, for every user. Most of it is the standalone dialog chunk (306,459 B raw, 95,486 B gzip -9), which React sites never load.ondocs/embed/standalonearedist/embed.d.ts, which importsReactNodeandMouseEventfromreact(through the options chunk). A TypeScript app without@types/reactgets unresolved types unless it setsskipLibCheck.Follow-ups
examples/starlightandexamples/vitepressstill depend on React, Radix and cmdk. Dropping them changes more ofpnpm-lock.yaml.<style>would break sites whose CSP has no'unsafe-inline'in style-src.