Skip to content

fix(adapter-static): resolve crawled links against the page, not the origin - #224

Merged
rturnq merged 1 commit into
mainfrom
dpiercey-ws-marko-website-3w77hx
Jul 30, 2026
Merged

fix(adapter-static): resolve crawled links against the page, not the origin#224
rturnq merged 1 commit into
mainfrom
dpiercey-ws-marko-website-3w77hx

Conversation

@DylanPiercey

@DylanPiercey DylanPiercey commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

The crawler resolved every href it found against the origin rather than the page it was found on, so a relative link lost its directory: ./concise-syntax on /docs/reference/language was crawled as /concise-syntax. That path 404s, and the crawler writes the 404 body out under it — a thin, indexable duplicate page for every relative link on the site. The inner const path shadowed the outer one, which is likely how this went unnoticed; it is now found.

Redirect Location headers resolve against the page for the same reason — per the fetch spec a relative location is relative to the request URL. resolveUrl now derives its same-origin check from the base instead of taking the origin as both base and comparison, so off-origin links are still skipped.

@changeset-bot

changeset-bot Bot commented Jul 28, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 9a4bdc1

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
@marko/run-adapter-static 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

@coderabbitai

coderabbitai Bot commented Jul 28, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

You’ve reached a temporary PR review limit under our Fair Usage Limits Policy.

Your recent review volume is higher than typical usage, so adaptive limits are currently applied.

Next review available in: 39 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 93fcfaca-9518-48ae-9611-f4ad58d36c29

📥 Commits

Reviewing files that changed from the base of the PR and between 52277cf and 9a4bdc1.

📒 Files selected for processing (2)
  • .changeset/crawler-relative-href-base.md
  • packages/adapters/static/src/crawler.ts

Walkthrough

The static crawler now resolves discovered relative links against the URL of the page containing each link. Redirect targets are also resolved against the current request URL, including pathname, search, and hash handling. URL helpers accept either string or URL bases and skip cross-origin results. Deduplication paths use the resolved page-relative destinations. A patch changeset documents the behavior.

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
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.
Title check ✅ Passed The title clearly matches the main change: resolving crawled links against the page instead of the origin.
Description check ✅ Passed The description accurately describes the crawler URL-resolution and redirect handling fixes in the changeset.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch dpiercey-ws-marko-website-3w77hx

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

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
packages/adapters/static/src/crawler.ts (1)

31-41: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add regression coverage for page-relative resolution.

Please cover ./sibling from /docs/reference/language, relative redirect targets with query/hash components, and off-origin targets remaining unenqueued. These paths are central to the PR behavior and currently have no unit coverage.

Also applies to: 91-98

🤖 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 `@packages/adapters/static/src/crawler.ts` around lines 31 - 41, Add regression
tests for the crawler’s page-relative URL resolution around resolvePath and
queueing: verify ./sibling from /docs/reference/language resolves to
/docs/reference/sibling, relative redirect targets preserve query and hash
components, and off-origin targets are not added to the queue. Cover both the
normal link path and the corresponding redirect handling noted in the comment.
🤖 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.

Inline comments:
In `@packages/adapters/static/src/crawler.ts`:
- Around line 90-94: The redirect handling around resolvePathWithHash must
reject a missing or blank Location header before resolving it against url. Only
call resolvePathWithHash and continue redirect processing when the header
contains a non-empty location; otherwise treat the 3xx response as an unhandled
redirect and avoid generating a self-redirect.

---

Nitpick comments:
In `@packages/adapters/static/src/crawler.ts`:
- Around line 31-41: Add regression tests for the crawler’s page-relative URL
resolution around resolvePath and queueing: verify ./sibling from
/docs/reference/language resolves to /docs/reference/sibling, relative redirect
targets preserve query and hash components, and off-origin targets are not added
to the queue. Cover both the normal link path and the corresponding redirect
handling noted in the comment.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 95238ee8-9069-4cf8-ad59-882e4baedb42

📥 Commits

Reviewing files that changed from the base of the PR and between 7462bf0 and 52277cf.

📒 Files selected for processing (2)
  • .changeset/crawler-relative-href-base.md
  • packages/adapters/static/src/crawler.ts

Comment thread packages/adapters/static/src/crawler.ts Outdated
@DylanPiercey
DylanPiercey force-pushed the dpiercey-ws-marko-website-3w77hx branch 2 times, most recently from 97e8319 to 076a4c3 Compare July 30, 2026 16:24
…origin

`visit` passed `origin` as the base for every href it found, so a relative
link lost the directory it was written in: `./concise-syntax` on
`/docs/reference/language` resolved to `/concise-syntax`. That path 404s, and
the crawler then wrote the 404 body out as `concise-syntax.html`, which a
static host serves at 200 -- a thin, indexable duplicate for every relative
link in the site.

The page URL was already in scope; the inner `const path` shadowed it, which
is likely how this went unnoticed. It is now the base, and the shadowing is
gone. Redirect `Location` headers resolve against it too, which is what the
fetch spec says a relative location means.

`origin` is now parsed once into a `URL`, so every base is a `URL` and
`resolveUrl` takes its same-origin check from `base.origin` instead of
re-parsing the base on every link.

Measured on markojs.com: 168 stray 404 pages against 57 real ones, of which
65 were doc-shaped. After the fix only one remains, and that one is a genuine
broken link in the docs -- now emitted at the path it is actually written at.
@DylanPiercey
DylanPiercey force-pushed the dpiercey-ws-marko-website-3w77hx branch from 076a4c3 to 9a4bdc1 Compare July 30, 2026 16:29
@rturnq
rturnq merged commit 8f4f0c3 into main Jul 30, 2026
9 checks passed
@rturnq
rturnq deleted the dpiercey-ws-marko-website-3w77hx branch July 30, 2026 16:38
@DylanPiercey DylanPiercey moved this to Done in Roadmap Jul 31, 2026
@DylanPiercey DylanPiercey self-assigned this Jul 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants