fix(cli): stop global-theme stitching from breaking or dropping js - #17575
fix(cli): stop global-theme stitching from breaking or dropping js#17575devin-ai-integration[bot] wants to merge 2 commits into
Conversation
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
There was a problem hiding this comment.
AI Review Summary
Small, targeted fix: plain-string presigned js entries are now downloaded, and cfg.js is no longer clobbered with [] when the theme declares none. Logic looks correct; one minor nit about the idx++ counter being incremented inside a .map callback (pre-existing pattern, but the new string branch makes it slightly more subtle).
- 🔵 2 suggestion(s)
To request another review, comment /ai-review on this pull request.
| jsList.map(async (entry) => { | ||
| // "..." → shorthand for a local file, uploaded like a css string | ||
| if (typeof entry === "string") { | ||
| return isPresignedUrl(entry) ? downloadToTemp(entry, tmpDir, idx++) : entry; |
There was a problem hiding this comment.
🔵 suggestion
idx++ is evaluated synchronously during .map, so ordering is deterministic — fine. Just note that if downloadToTemp uses idx only for uniqueness this is safe; if it's used for stable naming across runs, mixing string and {path} entries in one list still yields consistent indices. No change required, flagging so it stays intentional.
| // the consuming repo resolves it as a path relative to its own docs directory. | ||
| it("downloads a presigned plain-string JS entry", async () => { | ||
| const result = await resolveThemeFileUrls({ js: PRESIGNED_JS }, tmpDir); | ||
| expect(result.js).toEqual([path.join(tmpDir, "header-scroll.js")]); |
There was a problem hiding this comment.
🔵 suggestion
The presigned tests assert an exact filename derived from response-content-disposition, but the stubbed headers.get returns null. Worth a quick check that downloadToTemp derives the name from the URL query rather than the response header — otherwise this assertion may be passing for the wrong reason (e.g. falling back to idx-based naming that happens to match).
Docs Generation Benchmark ResultsComparing PR branch against median of 5 nightly run(s) on
Docs generation runs |
There was a problem hiding this comment.
AI Review Summary
Fix looks correct: plain-string presigned js entries are now downloaded, and cfg.js is left untouched when absent. One thing to verify is the idx++ counter semantics inside Promise.all map (pre-existing) and whether the normalization of a single string js into an array is safe downstream.
- 🔵 1 suggestion(s)
To request another review, comment /ai-review on this pull request.
| jsList.map(async (entry) => { | ||
| // "..." → shorthand for a local file, uploaded like a css string | ||
| if (typeof entry === "string") { | ||
| return isPresignedUrl(entry) ? downloadToTemp(entry, tmpDir, idx++) : entry; |
There was a problem hiding this comment.
🔵 suggestion
idx++ is evaluated synchronously inside the map callback, so it's still deterministic here — but note the mapping now also normalizes a scalar js: "..." into a one-element array even when nothing was downloaded. Confirm every downstream consumer of cfg.js accepts the array form (the tests assert it does, so presumably fine).
Co-Authored-By: Devin AI <158243242+devin-ai-integration[bot]@users.noreply.github.com>
Description
resolveThemeFileUrlsmishandledjsin two ways, both of which only surface at publish time —fern checkpasses either way.js: ./assets/x.jsuploads the file to CAS and gets a presigned URL back as a bare string. Only{ path }objects were downloaded, so the consumer joined the URL onto its own docs root:jsclobbered the consumer's ownjs— the resolver unconditionally wrotecfg.js = []when the theme declared nojs.jshas policy"global"inTHEME_FIELD_POLICIES, and[]is not null, so the theme-wins branch ofmergeThemeOverridereplaced the repo'sjs: ./assets/header-scroll.jswith[]. Any repo withglobal-theme:silently lost every script declared in its owndocs.yml; the registered docs config ended upjs: {files: [], remote: []}and no<script>was emitted.Changes Made
jsentries, matching thecssstring path.cfg.jsuntouched when the theme declares none, so the merge keeps the local value.{ url }, non-presigned strings) are still passed through unchanged.Testing
{ path }download, themelessjsstaying absent, and an end-to-endresolveThemeFileUrls→mergeThemeOverridecase asserting a consumer keepsjs: ./assets/header-scroll.js. The first and last fail onmainwith exactly the URL-as-path and the[]overwrite.Link to Devin session: https://app.devin.ai/sessions/5e37d55a6f354d3c9a3875a38f6ebf44
Open in Devin Desktop: https://app.devin.ai/desktop/session/5e37d55a6f354d3c9a3875a38f6ebf44?variant=devin