feat(docs): add DOCS_BASE_URL build arg for sub-path mounting#1684
Conversation
Vite, TanStack Router, the SSR entry, and the prerender script now all read the build-time DOCS_BASE_URL (defaulting to '/'), so the docs app can be mounted under a prefix like /docs/ without breaking absolute asset URLs. compose.docs.yml forwards the arg and the Dockerfile sets it as an env before vite runs. The previously hardcoded '/llms.txt' link in the footer and the '/images/...' icon references in index.html now use BASE_URL so they follow the configured mount point.
📝 WalkthroughWalkthroughThis PR makes the docs service deployable under a configurable base path. A new Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 2
🤖 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 `@services/docs/scripts/prerender.ts`:
- Line 18: BASE_PATH normalization only removes a trailing slash and can produce
values without a leading slash (e.g., "docs"); update the BASE_PATH
initialization so it produces either an empty string for root or a value that
always starts with '/' and has no trailing slash. Specifically, replace the
current BASE_PATH assignment with logic that reads process.env.DOCS_BASE_URL,
returns '' if falsy or equals '/', otherwise ensures the value begins with '/'
and does not end with '/', and keep references to BASE_PATH (used later around
line 105) intact.
In `@services/docs/vite.config.ts`:
- Around line 6-9: The Vite `base` is set directly from
process.env.DOCS_BASE_URL which can be inconsistent (e.g. "docs", "/docs",
"/docs/"); move normalization logic out of defineConfig() and before the `export
default` so it isn't declared inside the object literal, compute a canonical
base in a const (e.g. read process.env.DOCS_BASE_URL, ensure it starts and ends
with a single '/', default to '/'), then reference that const in the
`defineConfig({ base: ... })` assignment; update references to DOCS_BASE_URL,
`base`, and ensure the normalization function/const is declared at module scope
(not inside `defineConfig()`).
🪄 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: ASSERTIVE
Plan: Pro
Run ID: 51cf8e9b-235d-4103-993b-fe84e6ea406e
📒 Files selected for processing (8)
compose.docs.ymlservices/docs/Dockerfileservices/docs/app/components/docs/docs-footer.tsxservices/docs/app/entry-server.tsxservices/docs/app/router.tsxservices/docs/index.htmlservices/docs/scripts/prerender.tsservices/docs/vite.config.ts
| const SITE_URL = process.env.DOCS_SITE_URL ?? 'https://docs.tale.dev'; | ||
| // Mount-point prefix passed to the router during SSR so it resolves URLs | ||
| // against the same basepath the client uses. Empty for root deployments. | ||
| const BASE_PATH = (process.env.DOCS_BASE_URL ?? '/').replace(/\/$/, ''); |
There was a problem hiding this comment.
Harden BASE_PATH normalization to require a leading slash.
Line 18 only strips a trailing slash; DOCS_BASE_URL=docs becomes docs and Line 105 then renders relative URLs (docs/...). Normalize to '' (root) or '/segment'.
Suggested patch
-const BASE_PATH = (process.env.DOCS_BASE_URL ?? '/').replace(/\/$/, '');
+const rawBase = process.env.DOCS_BASE_URL ?? '/';
+const normalizedBase = rawBase.replace(/^\/+|\/+$/g, '');
+const BASE_PATH = normalizedBase.length === 0 ? '' : `/${normalizedBase}`;Also applies to: 105-105
🤖 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 `@services/docs/scripts/prerender.ts` at line 18, BASE_PATH normalization only
removes a trailing slash and can produce values without a leading slash (e.g.,
"docs"); update the BASE_PATH initialization so it produces either an empty
string for root or a value that always starts with '/' and has no trailing
slash. Specifically, replace the current BASE_PATH assignment with logic that
reads process.env.DOCS_BASE_URL, returns '' if falsy or equals '/', otherwise
ensures the value begins with '/' and does not end with '/', and keep references
to BASE_PATH (used later around line 105) intact.
| // Build-time mount point. Defaults to '/' (e.g. docs.tale.dev). Set to a | ||
| // sub-path with trailing slash like '/docs/' to serve the docs app under | ||
| // that prefix — Vite then prefixes every asset URL accordingly. | ||
| base: process.env.DOCS_BASE_URL ?? '/', |
There was a problem hiding this comment.
🧩 Analysis chain
🌐 Web query:
In Vite, what exact input format is recommended for the baseoption, and how doesimport.meta.env.BASE_URLbehave whenbase is provided without a leading or trailing slash?
💡 Result:
Recommended input format for Vite’s base option: 1) Use an absolute URL pathname that ends with a slash, e.g. /foo/ [1]. 2) You can also use a full URL pathname, e.g. https://bar.com/foo/ (the origin is ignored in development; the path part is used the same as /foo/) [1]. 3) For embedded deployment (relative), use '' or './' [1]. How import.meta.env.BASE_URL behaves when base is provided without a leading or trailing slash: - import.meta.env.BASE_URL is “the public base url the app is being served from” and it is “determined by the base config option” [2]. - Vite’s base option supports values like /foo/, and for relative base you can use '' or ./ [1]. The official configuration docs list valid formats using leading and trailing slashes for absolute bases (e.g. /foo/) [1]. - In Vite’s implementation history, the team specifically added support for “base without trailing slash” (PR titled “feat: base without trailing slash”) [3], meaning Vite can accept /foo as base (without the ending /) while still working as a base path. - Regarding whether the leading slash is required: Vite’s configuration resolution code emits a warning when the base option “should start with a slash”, indicating that omitting the leading slash is not the intended/validated format [4]. Practical takeaway: - Prefer setting base exactly in the documented/validated form: /subdir/ (leading and trailing slash) or .//'' (embedded) [1]. - If you omit the trailing slash (e.g. /subdir), Vite has support for it [3]. - If you omit the leading slash, Vite warns that it “should start with a slash”, so import.meta.env.BASE_URL may not match what you expect versus the documented absolute base formats [4][2].
Citations:
- 1: https://vitejs.dev/config/shared-options.html
- 2: https://vite.dev/guide/env-and-mode
- 3: feat: base without trailing slash vitejs/vite#10723
- 4: vitejs/vite@8f87282
🏁 Script executed:
fd -t f "vite.config.ts" | head -20Repository: tale-project/tale
Length of output: 205
🏁 Script executed:
cat -n services/docs/vite.config.tsRepository: tale-project/tale
Length of output: 3612
🏁 Script executed:
# Check other vite configs for env variable handling patterns
rg "process\.env\." services/platform/vite.config.ts services/web/vite.config.ts -A 2Repository: tale-project/tale
Length of output: 537
🏁 Script executed:
# Look for any utility functions for path/base normalization in the codebase
fd -t f "format" -o -t f "normalize" | grep -E "\.(ts|js)$" | head -20Repository: tale-project/tale
Length of output: 178
🏁 Script executed:
# Check if there's a shared utils or lib directory
ls -la lib/ 2>/dev/null || ls -la src/ 2>/dev/null | head -20Repository: tale-project/tale
Length of output: 43
🏁 Script executed:
cat -n services/platform/vite.config.ts | head -20Repository: tale-project/tale
Length of output: 1283
Normalize DOCS_BASE_URL before assigning Vite base, but fix the patch syntax.
Line 9 currently trusts raw env input. Values like docs or /docs can produce inconsistent URL prefixing across generated links/assets. Normalization to a canonical "/.../" shape is necessary.
However, the proposed patch has a critical syntax error: const declarations cannot be placed inside the defineConfig() object literal. Move them outside, before the export default, following the pattern already used in services/platform/vite.config.ts:
Corrected patch
+// Normalize env input to Vite's canonical base shape: "/" or "/segment/".
+// Accepts docs, /docs, /docs/ from deploy environments.
+const rawBase = process.env.DOCS_BASE_URL ?? '/';
+const base =
+ rawBase === '/'
+ ? '/'
+ : `/${rawBase.replace(/^\/+|\/+$/g, '')}/`;
+
export default defineConfig({
// Build-time mount point. Defaults to '/' (e.g. docs.tale.dev). Set to a
// sub-path with trailing slash like '/docs/' to serve the docs app under
// that prefix — Vite then prefixes every asset URL accordingly.
- base: process.env.DOCS_BASE_URL ?? '/',
+ base,📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Build-time mount point. Defaults to '/' (e.g. docs.tale.dev). Set to a | |
| // sub-path with trailing slash like '/docs/' to serve the docs app under | |
| // that prefix — Vite then prefixes every asset URL accordingly. | |
| base: process.env.DOCS_BASE_URL ?? '/', | |
| // Normalize env input to Vite's canonical base shape: "/" or "/segment/". | |
| // Accepts docs, /docs, /docs/ from deploy environments. | |
| const rawBase = process.env.DOCS_BASE_URL ?? '/'; | |
| const base = | |
| rawBase === '/' | |
| ? '/' | |
| : `/${rawBase.replace(/^\/+|\/+$/g, '')}/`; | |
| export default defineConfig({ | |
| // Build-time mount point. Defaults to '/' (e.g. docs.tale.dev). Set to a | |
| // sub-path with trailing slash like '/docs/' to serve the docs app under | |
| // that prefix — Vite then prefixes every asset URL accordingly. | |
| base, |
🤖 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 `@services/docs/vite.config.ts` around lines 6 - 9, The Vite `base` is set
directly from process.env.DOCS_BASE_URL which can be inconsistent (e.g. "docs",
"/docs", "/docs/"); move normalization logic out of defineConfig() and before
the `export default` so it isn't declared inside the object literal, compute a
canonical base in a const (e.g. read process.env.DOCS_BASE_URL, ensure it starts
and ends with a single '/', default to '/'), then reference that const in the
`defineConfig({ base: ... })` assignment; update references to DOCS_BASE_URL,
`base`, and ensure the normalization function/const is declared at module scope
(not inside `defineConfig()`).
Summary
Lets the docs app be deployed under a sub-path (e.g.
/docs/) without breaking absolute asset URLs. The motivating use case is the temporary deploy on the ops droplet wheredocs.tale.devDNS isn't set up yet, so both apps need to share a single hostname with web at/and docs at/docs.The default of
/keeps the existingdocs.tale.devdeployment unchanged.How it works
vite.config.tsreadsprocess.env.DOCS_BASE_URLand uses it as Vite'sbase(defaults to/). Vite then prefixes every generated asset URL (CSS chunks, JS chunks, fonts, imported images) with that value.index.htmluses Vite's%BASE_URL%placeholder for the favicon<link>tags and the/llms.txtalternate link.app/router.tsx+app/entry-server.tsxderive a TanStack Routerbasepathfromimport.meta.env.BASE_URL(stripping the trailing slash) so client-side and SSR routing both honour the prefix.scripts/prerender.tsprefixes the URL passed torender()with the samebasepathso memory-history-based SSR matches the same routes the client sees.docs-footer.tsxswaps a hardcoded/llms.txtfor${import.meta.env.BASE_URL}llms.txt.DockerfiledeclaresARG DOCS_BASE_URLand exports it asENVbeforevite buildruns.compose.docs.ymlforwardsDOCS_BASE_URLfrom the host env to the build args (defaults/).Pre-PR checklist
bun run check— not run by author; please verify locallyservices/platform/messages/{en,de,fr}.json— N/A (no UI strings changed)/docs/{en,de,fr}/— N/A (deployment infra only)bun run --filter @tale/docs lintandbun run --filter @tale/docs test— not run by author; please verify locallyREADME.md,README.de.md,README.fr.md— N/ATest plan
docker compose -f compose.docs.yml build— confirm thedocs.tale.devdeployment is unaffected (assets still at root).DOCS_BASE_URL=/docs/ docker compose -f compose.docs.yml build, then run behind a reverse proxy that strips/docsbefore forwarding. Confirm CSS, fonts, JS, favicon, andllms.txtall load under/docs/...and that<Link>clicks keep the prefix in the address bar.Summary by CodeRabbit