Skip to content

fix(docs): stop Fern bundling commented-out component imports - #12753

Closed
dagil-nvidia wants to merge 3 commits into
mainfrom
dagil-nvidia/docs-fern-component-import-scan
Closed

fix(docs): stop Fern bundling commented-out component imports#12753
dagil-nvidia wants to merge 3 commits into
mainfrom
dagil-nvidia/docs-fern-component-import-scan

Conversation

@dagil-nvidia

@dagil-nvidia dagil-nvidia commented Aug 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

The Preview or publish docs job has been failing on main and on every docs-touching PR with:

Failed to bundle third-party imports in fern/components/RecipeStyles.tsx:
rolldown exited with code 127 ... sh: 1: rolldown: not found

The named component rotated run to run — RecipeStyles.tsx, ReferenceStyles.tsx, TerminalDemo.tsx — and none of the three has an npm dependency. That is the tell: the trigger was not a real import.

Root cause. Fern collects a component's third-party dependencies with a plain-text regex over the whole source file. It does not parse the source, so it does not skip comments. Each of the three headers documented its own usage with a literal example statement naming @/components/<Name>. Fern read that comment as a real dependency; @/components is outside its allowlist (react, react-dom, @mdx-js/react, next), so it shelled out to npx rolldown to bundle it.

The docs-website branch ships no package.json next to fern/, so that bundle could never resolve. It surfaced two ways, which is what made it look flaky rather than broken:

  • exit 127rolldown: not found
  • exit 1ERR_MODULE_NOT_FOUND on a half-unpacked ~/.npm/_npx/…/rolldown/dist/shared/binding-*.mjs

Fern bundles the components concurrently, so several npx invocations race on one shared cache directory. A retry that happens to win the race goes green, which is why re-runs sometimes "fixed" it.

Fix. Document the specifier in prose in all three headers instead of writing a literal statement, and add docs/fern/scripts/check_component_imports.py — a port of Fern's own matcher (regex, allowlist, and package-root reduction taken from the pinned CLI) — wired into pre-commit so a re-added example is caught before merge instead of on the publish.

No page content, CSS, or component behaviour changes; the edits are comment-only.

Validation

  • Ported Fern's detector and ran it against docs/fern/components at main: flagged exactly RecipeStyles.tsx:17, ReferenceStyles.tsx:23, TerminalDemo.tsx:15,28 — the same three files CI named, and no others.
  • After the fix: checked 33 component source(s): no unbundleable specifiers.
  • Replayed the workflow's components sync (fern-docs.yml, the rm -rf + cp -r into docs-checkout/fern/components) against a real origin/docs-website worktree and re-ran the check on the composed tree — clean, 33/33. Confirmed docs-website carries exactly these 33 files, so the branch copy cannot drift.
  • Guard verified in both directions: it exits 1 with a located finding on the pre-fix RecipeStyles.tsx, and 0 on the fixed tree.
  • --test self-test covers 12 cases, including allowlisted, scoped-allowlisted, subpath, relative, dynamic import(), require(), re-export, the commented-example regression, and prose that merely contains the word.
  • pre-commit run --files … green on all five changed files.

Not fixed here, and tracked separately: main is also red on the later Verify published pages carry their component CSS step, which 404s on /dynamo/dev/recipes/feature-benchmarks/llama-3-3-70b-topology. That is a different failure in a different step — Publish Docs itself succeeds on main.

🤖 Generated with Claude Code


Open in Devin Review

Summary by CodeRabbit

  • Documentation

    • Clarified how to use styling and terminal demonstration components in applicable pages.
    • Added guidance for component imports, page placement, and runtime asset loading.
  • Quality Improvements

    • Added automated validation to detect unsupported component dependencies and provide actionable error details.
    • Included self-checks to help ensure dependency validation remains reliable.

The "Preview or publish docs" job failed intermittently with:

  Failed to bundle third-party imports in fern/components/RecipeStyles.tsx:
  rolldown exited with code 127 ... sh: 1: rolldown: not found

The named component rotated between RecipeStyles, ReferenceStyles and
TerminalDemo run to run, and none of the three has an npm dependency.

Fern collects a component's third-party dependencies with a plain-text
regex over the whole source file, so it does not skip comments. Each of
the three headers documented its own usage with a literal example
statement naming "@/components/<Name>". Fern read that as a real
dependency, and since "@/components" is outside its allowlist (react,
react-dom, @mdx-js/react, next) it shelled out to `npx rolldown` to
bundle it. The docs-website branch ships no package.json next to fern/,
so that bundle could never resolve; it failed with exit 127 or a
partially-unpacked npx cache, which is what made it look flaky.

Document the specifier in prose instead, and add
check_component_imports.py -- a port of Fern's own matcher, wired into
pre-commit -- so a re-added example is caught before merge rather than
on the publish.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Dan Gil <dagil@nvidia.com>
@dagil-nvidia
dagil-nvidia requested review from a team as code owners August 6, 2026 13:06
@dagil-nvidia

Copy link
Copy Markdown
Collaborator Author

/ok to test bc08dba

@github-actions github-actions Bot added fix documentation Improvements or additions to documentation labels Aug 6, 2026

@devin-ai-integration devin-ai-integration Bot left a comment

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.

Devin Review found 2 potential issues.

Open in Devin Review

if package_root(specifier) in ALLOWLIST or specifier in seen:
continue
seen.add(specifier)
found.append((contents[: match.start()].count("\n") + 1, specifier))

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.

🟡 Docs check reports the wrong line number for offending import statements

The reported location is computed from the character just before the statement (match.start() at docs/fern/scripts/check_component_imports.py:98), which is the newline ending the previous line whenever a statement begins at the start of a line, so the tool points contributors at the line above the real problem.
Impact: Someone fixing a failing docs check is sent to the wrong line, wasting time and potentially editing unrelated content.

Why the leading-character alternation shifts the line count

The pattern begins with (?:^|[^\w.]) (docs/fern/scripts/check_component_imports.py:68). For a statement at column 0 on any line after the first, the regex engine reaches the preceding \n before it reaches the line-start anchor position, so [^\w.] consumes that newline and match.start() points at it. contents[: match.start()].count("\n") + 1 therefore excludes that newline and yields the previous line's number.

Verified: for "header\nimport { X } from \"@/components/X\";\n" the function returns line 1 instead of 2. Indented comment examples (like the ones this PR removed) happen to be preceded by a space on the same line, which is why the author's validation showed correct numbers.

Using the specifier group's start position instead avoids the shift.

Suggested change
found.append((contents[: match.start()].count("\n") + 1, specifier))
start = match.start(1) if match.group(1) else match.start(2 if match.group(2) else 3)
found.append((contents[:start].count("\n") + 1, specifier))
Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +40 to +42
The regex and allowlist below mirror the Fern CLI's own implementation
(cli.cjs, the helper behind "Failed to bundle third-party imports"); keep them
in sync when the pinned CLI in fern.config.json moves.

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.

🔍 Guard regex is a hand-copy of Fern's matcher and can drift with the pinned CLI

The docstring states the regex and allowlist mirror the pinned Fern CLI's implementation and must be re-synced when fern.config.json moves. There is no automated check for that drift, so a CLI bump that widens the allowlist or changes the scan (e.g. to a real parser that skips comments) would leave this hook either over- or under-reporting. Worth a follow-up note in the docs release process.

Open in Devin Review

Was this helpful? React with 👍 or 👎 to provide feedback.

@github-actions

github-actions Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

The pull request adds a Fern component import checker with self-tests, documents valid component usage, and adds pre-commit hooks for validation.

Changes

Fern component import validation

Layer / File(s) Summary
Import checker implementation
docs/fern/scripts/check_component_imports.py
Adds Fern-compatible import detection, allowlisting, source discovery, diagnostics, self-tests, and exit handling.
Component usage documentation
docs/fern/components/RecipeStyles.tsx, docs/fern/components/ReferenceStyles.tsx, docs/fern/components/TerminalDemo.tsx
Documents named imports, page placement, @/ path resolution, CDN-loaded assets, and the restriction on literal import statements.
Pre-commit enforcement
.pre-commit-config.yaml
Adds hooks for component import validation and checker self-tests.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description explains the root cause, fix, and validation, but it omits the required Related Issues section and reviewer starting point. Add the required Related Issues section and identify where the reviewer should start, using the repository template headings.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the primary fix for Fern bundling commented-out component imports.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

🧹 Nitpick comments (1)
docs/fern/scripts/check_component_imports.py (1)

110-119: 🩺 Stability & Availability | 🔵 Trivial | ⚡ Quick win

Specify an explicit encoding for read_text().

path.read_text() at line 111 uses the platform default encoding when no encoding argument is given. On a system where the locale-preferred encoding is not UTF-8 (for example Windows), reading a component source file containing non-ASCII characters can raise UnicodeDecodeError and crash the pre-commit hook instead of reporting the specifier problem.

Pass encoding="utf-8" explicitly to make the read deterministic across platforms.

🔧 Proposed fix
 def check(path: Path) -> list[str]:
-    text = path.read_text()
+    text = path.read_text(encoding="utf-8")
     return [
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@docs/fern/scripts/check_component_imports.py` around lines 110 - 119, Update
the file read in check to pass encoding="utf-8" explicitly to path.read_text(),
ensuring deterministic decoding across platforms while leaving the third_party
processing unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@docs/fern/scripts/check_component_imports.py`:
- Around line 110-119: Update the file read in check to pass encoding="utf-8"
explicitly to path.read_text(), ensuring deterministic decoding across platforms
while leaving the third_party processing unchanged.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 5f5f0262-4a6c-4bd6-8607-925b1e925ca2

📥 Commits

Reviewing files that changed from the base of the PR and between 06045a2 and bc08dba.

📒 Files selected for processing (5)
  • .pre-commit-config.yaml
  • docs/fern/components/RecipeStyles.tsx
  • docs/fern/components/ReferenceStyles.tsx
  • docs/fern/components/TerminalDemo.tsx
  • docs/fern/scripts/check_component_imports.py

- Anchor reported line numbers on the matched specifier group rather than
  match.start(). The first alternation opens with (?:^|[^\w.]), which
  consumes the preceding newline for a statement at column 0, so the guard
  pointed at the line above. Only the import/export branch was affected;
  require() and import() carry no leading alternation. Self-test cases now
  assert the line number, including two column-0 regressions.

- Fail a full scan when fern.config.json is bumped past the CLI release the
  matcher was ported from. The regex and allowlist are a hand-copy of Fern's
  bundler, and a CLI change that widens the allowlist or starts skipping
  comments would leave this check silently under-reporting -- which breaks
  the docs publish exactly the way the check exists to prevent. The
  pre-commit hook now watches fern.config.json so the bump trips it.

Both raised by devin-ai-integration[bot] on #12753.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Dan Gil <dagil@nvidia.com>
@dagil-nvidia

Copy link
Copy Markdown
Collaborator Author

/ok to test c52241b

path.read_text() decodes with the locale-preferred encoding. 28 of the 33
scanned component sources are non-ASCII (em dashes in the doc headers), so
under a non-UTF-8 locale the hook died with UnicodeDecodeError instead of
reporting the specifier problem it exists to catch. Verified: under LC_ALL=C
a bare read of RecipeStyles.tsx raises on byte 0xe2, and the explicit read
succeeds.

Raised by coderabbitai on #12753.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Signed-off-by: Dan Gil <dagil@nvidia.com>
@dagil-nvidia

Copy link
Copy Markdown
Collaborator Author

/ok to test 51869fd

@dagil-nvidia

Copy link
Copy Markdown
Collaborator Author

Closing in favor of #12388, which fixes the same problem and does more.

Both PRs independently rewrote the doc-comment import examples in `RecipeStyles.tsx`, `ReferenceStyles.tsx` and `TerminalDemo.tsx`, and both added a `docs/fern/scripts/check_component_imports.py` ported from the same fern-api 5.80.2 bundler. #12388 predates this one by eight days; I did not know it existed when I opened this.

I verified the two are equivalent before choosing, rather than assuming the newer one was better:

  • Run against `origin/main`, both checkers report the same four specifiers — `@/components/RecipeStyles`, `@/components/ReferenceStyles`, `asciinema-player`, `@/components/TerminalDemo`.
  • Each checker passes its own tree, and each also passes the other PR's components. Neither fix is incomplete relative to the other.

#12388 additionally wires the hook into `.github/workflows/pre-merge.yml` and relocates the usage examples into `docs/fern/components/README.md`, where Fern's scan cannot reach them.

Two things from this PR are genuinely better and I will port them onto #12388 rather than lose them: the finding output carries file:line instead of naming only the file, and the remediation text explains the CDN-at-runtime path for a real dependency.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation fix size/L

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant