Skip to content

Commit 14394d0

Browse files
committed
feat: Use README.md as placeholder pages for sections
Implemented automatic detection and use of README.md files as section placeholder pages in Confluence. This provides a much better user experience than using the first child page to represent a section. How it works: 1. After parsing nav, detect sections and their children 2. Infer section directory from children's file paths 3. Look for README.md in that directory 4. If found, create a page for it with the section title 5. Use README page as parent for section children 6. Prioritize README pages by sorting them first during sync Example: docs/ subscriptions/ README.md ← Becomes "Subscriptions" page overview.md development/ README.md ← Becomes "Development" page arch.md Result in Confluence: Home └─ Subscriptions (from subscriptions/README.md) ├─ Overview └─ Development (from subscriptions/development/README.md) └─ Architecture Benefits: - Section pages have meaningful content from README.md - Better matches MkDocs/GitHub documentation patterns - Falls back to first child if no README.md exists - Fully backward compatible All tests passing (148/148).
1 parent 50974fa commit 14394d0

10 files changed

Lines changed: 123 additions & 5 deletions

File tree

dist/index.js

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

lib/confluence-syncer.js

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -114,7 +114,7 @@ async function findParentPage() {
114114
*/
115115
async function syncPages(home, localPages, sectionHierarchy, renderer) {
116116
// Map to track synced page IDs
117-
// For sections, we store the ID of the first page that represents that section
117+
// For sections, we store the ID of the section's README or first page
118118
const syncedPages = new Map();
119119
syncedPages.set(null, home); // root level pages use home as parent
120120

@@ -134,7 +134,7 @@ async function syncPages(home, localPages, sectionHierarchy, renderer) {
134134
const pages = pagesByParent.get(sectionName) || [];
135135

136136
// Find the parent page ID for this section
137-
// If the section has a parent section, use the ID of that parent section's first page
137+
// If the section has a parent section, use the ID of that parent section's page
138138
// Otherwise, use the home page
139139
const parentSectionName = sectionHierarchy[sectionName] || null;
140140
const parentPageId = syncedPages.get(parentSectionName);
@@ -164,12 +164,21 @@ async function syncPages(home, localPages, sectionHierarchy, renderer) {
164164
union.push(remotePage);
165165
}
166166

167+
// Sort pages to prioritize README.md (section placeholder) first
168+
union.sort((a, b) => {
169+
const aIsReadme = a.meta?.path?.endsWith('README.md') || false;
170+
const bIsReadme = b.meta?.path?.endsWith('README.md') || false;
171+
if (aIsReadme && !bIsReadme) return -1;
172+
if (!aIsReadme && bIsReadme) return 1;
173+
return 0;
174+
});
175+
167176
// Sync all pages at this level
168177
for (let i = 0; i < union.length; i++) {
169178
const page = union[i];
170179
const syncedPage = await page.sync(renderer, confluence);
171180

172-
// The first page in this section represents the section
181+
// The first page (preferably README) represents the section
173182
// Store its ID so child sections can reference it
174183
// Don't overwrite the root level (null) which is set to home page ID
175184
if (i === 0 && sectionName !== null && syncedPage && syncedPage.id) {

lib/context.js

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,13 @@ function getContext(basePath = '.') {
9999
const sectionHierarchy = new Map();
100100
const pages = traverse(repo_url, nav, basePath, [], null, sectionHierarchy);
101101
const readMe = getPage(repo_url, site_name, path.resolve(basePath, README_MD), '');
102+
103+
// Create README.md pages for sections
104+
const sectionPages = createSectionPages(repo_url, basePath, pages, sectionHierarchy);
105+
106+
// Add section pages to the main pages array
107+
pages.push(...sectionPages);
108+
102109
const pageRefs = pages.reduce((obj, page) => {
103110
obj[page.meta.path] = page.title;
104111
return obj;
@@ -118,4 +125,88 @@ function getContext(basePath = '.') {
118125
return context;
119126
}
120127

128+
/**
129+
* Create pages for section README.md files
130+
*
131+
* @param {string} repo_url - Repository URL
132+
* @param {string} basePath - Base path to resolve files
133+
* @param {Array<LocalPage>} pages - Array of pages
134+
* @param {Map} sectionHierarchy - Section hierarchy map
135+
* @returns {Array<LocalPage>} Array of section README pages
136+
*/
137+
function createSectionPages(repo_url, basePath, pages, sectionHierarchy) {
138+
const sectionPages = [];
139+
140+
// For each section, try to find its README.md
141+
for (let [sectionName, parentSection] of sectionHierarchy.entries()) {
142+
// Find all pages that belong to this section
143+
const sectionChildren = pages.filter(p => p.parentPath === sectionName);
144+
145+
if (sectionChildren.length === 0) continue;
146+
147+
// Infer the section directory from children's paths
148+
const sectionDir = inferSectionDirectory(sectionChildren);
149+
150+
if (sectionDir) {
151+
// Try to find README.md in this directory
152+
const readmePath = path.resolve(basePath, 'docs', sectionDir, 'README.md');
153+
const readmePage = getPage(repo_url, sectionName, readmePath, config.confluence.titlePrefix);
154+
155+
if (readmePage) {
156+
readmePage.parentPath = parentSection;
157+
sectionPages.push(readmePage);
158+
logger.debug(`Created section page "${sectionName}" from ${sectionDir}/README.md`);
159+
}
160+
}
161+
}
162+
163+
return sectionPages;
164+
}
165+
166+
/**
167+
* Infer the common directory path from a section's children
168+
*
169+
* @param {Array<LocalPage>} children - Array of child pages
170+
* @returns {string|null} Common directory path or null
171+
*/
172+
function inferSectionDirectory(children) {
173+
if (children.length === 0) return null;
174+
175+
// Get relative paths of all children from their absolute paths
176+
const relativePaths = children.map(child => {
177+
const fullPath = child.meta.path;
178+
// Find 'docs/' in the path and get everything after it
179+
const docsIndex = fullPath.indexOf('/docs/');
180+
if (docsIndex === -1) return null;
181+
182+
const afterDocs = fullPath.substring(docsIndex + 6); // +6 for '/docs/'
183+
// Get the directory part (remove filename)
184+
return path.dirname(afterDocs);
185+
}).filter(p => p !== null);
186+
187+
if (relativePaths.length === 0) return null;
188+
189+
// Find common prefix directory
190+
const firstPath = relativePaths[0];
191+
192+
// If all paths are the same, return that path
193+
if (relativePaths.every(p => p === firstPath)) {
194+
return firstPath;
195+
}
196+
197+
// Find longest common prefix
198+
const parts = firstPath.split(path.sep);
199+
200+
for (let len = parts.length; len > 0; len--) {
201+
const prefix = parts.slice(0, len).join(path.sep);
202+
const allMatch = relativePaths.every(p => p.startsWith(prefix));
203+
204+
if (allMatch) {
205+
return prefix;
206+
}
207+
}
208+
209+
return null;
210+
}
211+
121212
export default { getContext };
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Home
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Subscriptions
2+
3+
This is the subscriptions section overview.
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# Development Environment
2+
3+
Development environment details.
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Development Architecture
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
# Overview
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
site_name: Test With Section READMEs
2+
repo_url: https://github.com/test/repo
3+
4+
nav:
5+
- Home: index.md
6+
- Subscriptions:
7+
- Overview: subscriptions/overview.md
8+
- Development:
9+
- Dev Arch: subscriptions/development/arch.md

0 commit comments

Comments
 (0)