fix(core): remove implicit file-promotion + clean dev output + full rebuild on watch#133
Merged
mgks merged 3 commits intoMay 16, 2026
Merged
Conversation
When a folder contains .md files but no index.md, the auto-router
designated the first file (alphabetically by title) as the folder-level
index by overwriting its path with basePath. This had two problems:
1. Silent URL mismatch (fixed in the previous commit via the generator.ts
trailing-slash fix) — the nav link pointed to the folder URL while the
file was still output at its own path, producing a 404 on click.
2. Even when working correctly (after the generator.ts fix), the promotion
drops the filename from the URL entirely:
docs/SA/feature-discovery/my-doc.md → /SA/feature-discovery/
instead of the expected:
docs/SA/feature-discovery/my-doc.md → /SA/feature-discovery/my-doc/
This creates inconsistent URL behaviour: files in folders WITH index.md
keep their filename in the URL; files in folders WITHOUT index.md do not.
It also makes it impossible to distinguish between two same-named files
in sibling folders by URL alone.
Fix: remove the promotion block so every .md file always generates a URL
that includes its own filename, regardless of whether the parent folder
has an index.md.
Before:
docs/SA/feature-discovery/my-doc.md → nav: /SA/feature-discovery
→ output: site/SA/feature-discovery/index.html
After:
docs/SA/feature-discovery/my-doc.md → nav: /SA/feature-discovery/my-doc/
→ output: site/SA/feature-discovery/my-doc/index.html
Impact: folders without index.md are rendered as collapsible section
headers in the sidebar (no link) rather than having the first child file
silently represent the section. This is more explicit and predictable.
The _sourceFile field on nav items is no longer set; the corresponding
navDesignatedIndexFiles logic in generator.ts becomes a no-op (harmless).
Two dev server bugs that degrade the development experience: 1. Stale output files persist across dev restarts buildSite used fs.ensureDir (mkdir -p) which never removes existing files. After changing auto-router behaviour (e.g. from this PR), old HTML files built under the previous URL structure remain in site/. The new nav links point to new URLs, but the old files at old paths still exist and the new files may not yet exist, causing 404 on nav click. Fix: call fs.remove(rootOutputDir) before the initial build so every dev session starts from a clean slate. 2. Watcher only rebuilds the changed file — nav goes stale The file watcher called buildSite with targetFiles: [filePath], which regenerates only the modified page. Every other page keeps its old nav HTML. Adding a new .md file while the dev server is running means the link to that file never appears in the sidebar of existing pages. Fix: remove targetFiles so every watched change triggers a full rebuild. For typical documentation projects (~20-50 pages) a full rebuild takes ~200-300 ms — imperceptible in practice.
Files and folders whose names contain spaces or URL-unsafe characters produced 404 in both dev and production: nav link → './folder with space/page.md/' (literal space) browser → /folder%20with%20space/page/ (encodes space as %20) server → tries 'folder%20with%20space/' → file not found → 404 Root cause: normalizeInternalHref adds a trailing slash but does not encode/slugify the path segments. The OS filename is used verbatim in the nav href and the site/ output path. When the browser URL-encodes the space, the path no longer matches the filename on disk. Fix: add a slugifySegment helper that converts spaces and URL-unsafe characters to hyphens, and apply it in two places: auto-router.ts — when building each relPath segment from fs.readdirSync generator.ts — when building htmlOutputPath from relativePath Both sides use identical logic, so the nav URL and the output file path always agree. Slugification rules: spaces → '-' [^a-zA-Z0-9\-_.~] → '-' consecutive hyphens → single hyphen leading/trailing hyphens stripped fallback: keep original name if slugified result is empty Files with already-safe names are unaffected (no-op). Before: 'docs/SA/my feature/page one.md' → 404 After: nav: /SA/my-feature/page-one/ → 200 ✓
Member
|
Tested with new workspace changes and playground. Everything works smoothly and feels robust. Great job! Thank you for the contribution. |
mgks
added a commit
that referenced
this pull request
May 16, 2026
…ents - Introduced new workspace engine with multi-project build support. - Added Project Switcher UI with slim pill-style design. - Fixed absolute URL resolution and prefix nesting in workspace mode. - Optimized dev server logs to be quieter during file/config reloads. - Integrated PR #133: workspace engine refinements and cleanup. - Improved i18n manifest generation for multi-project deployments.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Background
This PR follows #131 which fixed a silent 404 caused by a trailing-slash mismatch in
generator.ts. While investigating that bug, three related issues were found.Fix 1 — Remove implicit file-promotion in
auto-router.tsProblem
When a folder contains
.mdfiles but noindex.md,buildAutoNavsilently "promotes" the first file (alphabetically by title) to use the folder URL:This causes two problems:
Silent 404 (root cause of #131): nav link → folder URL, file output → own path, no
index.htmlat folder URL → 404 on click. Fixed in #131, but the root cause was here.Filename silently dropped from URL (still present after #131 if promotion is kept):
Instead of the intuitive:
This creates inconsistent URL behaviour:
index.md→ URL includes filename ✓index.md→ URL drops filename ✗It also makes same-named files in sibling folders indistinguishable by URL.
Fix
Remove the promotion block. Every
.mdfile always keeps its own filename in the URL.Before:
After:
Folders without
index.mdbecome collapsible section headers (no link) rather than having the first child silently represent the section.Migration: add an
index.mdto any folder that should have its own landing page.Fix 2 — Clean output dir before initial dev build
Problem
buildSiteusedfs.ensureDir(mkdir -p) which never removes existing files. After any change to auto-router behaviour (e.g. applying this PR), old HTML files built under the previous URL structure remain insite/. The new nav links point to the new URLs but the old files still exist at old paths — and the new files may not yet — causing 404 on nav click.Fix
Call
fs.remove(rootOutputDir)before the initial dev build so everydocmd devsession starts from a clean slate.Fix 3 — Full rebuild on file watch (nav goes stale with targeted rebuild)
Problem
The file watcher called
buildSitewithtargetFiles: [filePath], regenerating only the modified page. Every other page kept its old nav HTML. Adding a new.mdfile while the dev server is running means the link to that new file never appears in the sidebar of existing pages — the new page is accessible by direct URL but invisible in navigation.Fix
Remove
targetFilesfrom the watcher rebuild so every watched change triggers a full rebuild. For typical documentation projects (~20–50 pages) a full rebuild takes ~200–300 ms — imperceptible in practice.Note on
_sourceFile/navDesignatedIndexFilesWith Fix 1,
_sourceFileis never set on nav items, so thenavDesignatedIndexFileslogic ingenerator.tsbecomes a no-op. The code is left in place (harmless) as it may be useful for future features.