Skip to content

feat(cli-build): preconnect and modulepreload the CDN sanity module for auto-update studios#1276

Draft
jordanl17 wants to merge 3 commits into
mainfrom
feat/auth-ready-cdn-resource-hints
Draft

feat(cli-build): preconnect and modulepreload the CDN sanity module for auto-update studios#1276
jordanl17 wants to merge 3 commits into
mainfrom
feat/auth-ready-cdn-resource-hints

Conversation

@jordanl17

@jordanl17 jordanl17 commented Jun 11, 2026

Copy link
Copy Markdown
Member

Description

For auto-updating studios the main sanity module is loaded cross-origin from the CDN, and the browser only discovers it needs that module after it has downloaded and started executing the small entry script - a serial waterfall on every load. This adds two resource hints, emitted from inside the existing runtime import-map injector script, so the CDN connection is warmed and the module download begins during HTML parse instead of after entry execution.

Inside TIMESTAMPED_IMPORTMAP_INJECTOR_SCRIPT (after the existing CSS-link loop) it now appends a preconnect to the CDN module origin (with crossorigin) and a modulepreload for the sanity module. The origin is derived dynamically from the first import whose hostname matches the sanity-cdn pattern, so production (sanity-cdn.com) and staging (sanity-cdn.work) both work with no hardcoded host. The modulepreload href reuses the same runtime replaceTimestamp(...) value as the import map, so the timestamps match and the largest chunk is not fetched twice.

Scoped to auto-updating studios by construction: the injector is only added when an import map is present, and hints are only emitted when an import resolves to a sanity-cdn host. Non-auto-update studios are unchanged.

Background: this is the one quick win from a wider investigation into Studio auth-ready p95. Expected saving is modest (roughly 0.25-0.8s per auto-update load) because the dominant pre-mount cost is CPU parse/execute, not download.

What to review

The single production change is in packages/@sanity/cli-build/src/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.ts - only the inline injector script string. Confirm the no-importMap early return and the CSS loop are unchanged, that the preconnect targets the module host (sanity-cdn.com) and not the bridge host (core.sanity-cdn.com), and that the modulepreload href is computed with the same timestamp as the import map.

One open item to verify on a deployed auto-updating preview before this is marked ready: in DevTools, confirm the sanity module is fetched exactly once (not twice) and the modulepreload timestamp matches the import map entry. The modulepreload is currently emitted without crossorigin (the preconnect has it); if a deployed check shows a double fetch, adding crossorigin to the modulepreload is the fix. This cannot be reproduced on a local build, which is why the PR is a draft.

Testing

Four new JSDOM runtime tests in addTimestampImportMapScriptToHtml.test.ts assert against the executed DOM: preconnect emitted with crossorigin to the module origin; modulepreload href carries the fresh timestamp and equals the import map's resolved sanity entry (the no-double-fetch guarantee); no hints when no import resolves to a sanity-cdn host; and staging host support. All 16 tests in the file pass (12 existing + 4 new), and a mutation check confirmed the new tests fail when the hint code is removed. Build, type-check, lint, and format all pass.

How this fits

Part of the studio startup performance effort. This one targets auto-update studios only: warming the CDN connection and starting the module download during HTML parse. Independent of the other startup PRs.

@github-actions

github-actions Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

📦 Bundle Stats — @sanity/cli

Compared against main (4d4ed94c)

@sanity/cli

Metric Value vs main (4d4ed94)
Internal (raw) 2.1 KB -
Internal (gzip) 799 B -
Bundled (raw) 11.13 MB -
Bundled (gzip) 2.10 MB -
Import time 873ms +11ms, +1.2%

bin:sanity

Metric Value vs main (4d4ed94)
Internal (raw) 782 B -
Internal (gzip) 423 B -
Bundled (raw) 9.87 MB -
Bundled (gzip) 1.77 MB -
Import time 1.97s +9ms, +0.5%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — @sanity/cli-core

Compared against main (4d4ed94c)

Metric Value vs main (4d4ed94)
Internal (raw) 98.2 KB -
Internal (gzip) 23.3 KB -
Bundled (raw) 21.70 MB -
Bundled (gzip) 3.45 MB -
Import time 775ms +8ms, +1.1%

🗺️ View treemap · Artifacts

Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

📦 Bundle Stats — create-sanity

Compared against main (4d4ed94c)

Metric Value vs main (4d4ed94)
Internal (raw) 908 B -
Internal (gzip) 483 B -
Bundled (raw) 931 B -
Bundled (gzip) 491 B -
Import time ❌ ChildProcess denied: node -
Details
  • Import time regressions over 10% are flagged with ⚠️
  • Sizes shown as raw / gzip 🗜️. Internal bytes = own code only. Total bytes = with all dependencies. Import time = Node.js cold-start median.

@github-actions

Copy link
Copy Markdown
Contributor

Coverage Delta

File Statements
packages/@sanity/cli-build/src/actions/build/renderDocumentWorker/addTimestampImportMapScriptToHtml.ts 100.0% (±0%)

Comparing 1 changed file against main @ 4d4ed94c3c903f283940dbc19884990e4c19411f

Overall Coverage

Metric Coverage
Statements 81.9% (+ 0.5%)
Branches 73.2% (+ 0.5%)
Functions 81.2% (+ 1.4%)
Lines 82.4% (+ 0.5%)

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