Skip to content

fix(core): remove implicit file-promotion + clean dev output + full rebuild on watch#133

Merged
mgks merged 3 commits into
docmd-io:mainfrom
sinsombat:fix/auto-router-remove-file-promotion
May 16, 2026
Merged

fix(core): remove implicit file-promotion + clean dev output + full rebuild on watch#133
mgks merged 3 commits into
docmd-io:mainfrom
sinsombat:fix/auto-router-remove-file-promotion

Conversation

@sinsombat
Copy link
Copy Markdown
Contributor

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.ts

Problem

When a folder contains .md files but no index.md, buildAutoNav silently "promotes" the first file (alphabetically by title) to use the folder URL:

// current behaviour
if (!indexExists && nav.length > 0) {
  const firstFile = fileItems[0];
  firstFile._sourceFile = firstFile.path;  // save original
  firstFile.path = basePath;               // overwrite with folder URL
}

This causes two problems:

Silent 404 (root cause of #131): nav link → folder URL, file output → own path, no index.html at 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):

docs/SA/feature-discovery/my-doc.md  →  URL: /SA/feature-discovery/

Instead of the intuitive:

docs/SA/feature-discovery/my-doc.md  →  URL: /SA/feature-discovery/my-doc/

This creates inconsistent URL behaviour:

  • Files in folders with index.md → URL includes filename ✓
  • Files in folders without index.md → URL drops filename ✗

It also makes same-named files in sibling folders indistinguishable by URL.

Fix

Remove the promotion block. Every .md file always keeps its own filename in the URL.

Before:

nav link → /SA/feature-discovery
output   → site/SA/feature-discovery/index.html

After:

nav link → /SA/feature-discovery/my-doc/
output   → site/SA/feature-discovery/my-doc/index.html

Folders without index.md become collapsible section headers (no link) rather than having the first child silently represent the section.

Migration: add an index.md to any folder that should have its own landing page.


Fix 2 — Clean output dir before initial dev build

Problem

buildSite used fs.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 in site/. 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 every docmd dev session starts from a clean slate.


Fix 3 — Full rebuild on file watch (nav goes stale with targeted rebuild)

Problem

The file watcher called buildSite with targetFiles: [filePath], regenerating only the modified page. Every other page kept its old nav HTML. Adding a new .md file 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 targetFiles from 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 / navDesignatedIndexFiles

With Fix 1, _sourceFile is never set on nav items, so the navDesignatedIndexFiles logic in generator.ts becomes a no-op. The code is left in place (harmless) as it may be useful for future features.

sinsombat added 3 commits May 16, 2026 14:29
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 ✓
@mgks mgks merged commit c449ae4 into docmd-io:main May 16, 2026
1 check passed
@mgks
Copy link
Copy Markdown
Member

mgks commented May 16, 2026

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.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants