This is an automated email from the ASF dual-hosted git repository.

davsclaus pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/camel-website.git


The following commit(s) were added to refs/heads/main by this push:
     new 51169998 Fix #1762: publish a Markdown mirror of the website pages as 
<page>/index.md
51169998 is described below

commit 51169998a40699878dbdd911f1c4cd2022579679
Author: Claus Ibsen <[email protected]>
AuthorDate: Fri Sep 18 15:21:09 2026 +0200

    Fix #1762: publish a Markdown mirror of the website pages as <page>/index.md
    
    generate-markdown skipped every index.html, which is how Hugo renders each
    website page, so "What is Apache Camel", "When to use", the trust page,
    Camel DNA and the community pages had no Markdown mirror, nor had the
    Antora pages rendered as index.html. An index.html is now converted when
    it is a content page (has an article.doc); list and section pages are
    still skipped. The mirror sits next to the page as <page>/index.md so
    relative links stay valid. The embedded table of contents is dropped, and
    llms.txt documents the rule and links the first-reader pages as Markdown
    in a "Start here" section.
    
    Closes #1769
---
 gulp/tasks/generate-markdown.js | 36 ++++++++++++++++++++++++++----------
 llms-txt-template.md            | 15 ++++++++++-----
 test/generate-markdown-test.js  | 25 +++++++++++++++++++++++++
 3 files changed, 61 insertions(+), 15 deletions(-)

diff --git a/gulp/tasks/generate-markdown.js b/gulp/tasks/generate-markdown.js
index 8c210a03..eccd1f6a 100644
--- a/gulp/tasks/generate-markdown.js
+++ b/gulp/tasks/generate-markdown.js
@@ -16,6 +16,11 @@ const { generateAllIndexes } = 
require('../helpers/html-index');
  * - Only the main article content (excluding nav, header, footer)
  * - Clean Markdown formatting using Turndown
  * - GitHub-flavored Markdown for tables and code blocks
+ *
+ * Hugo renders every website page as <page>/index.html, so an index.html is 
converted too
+ * when it is a content page (has an article.doc): the .md then sits next to 
it as
+ * <page>/index.md, which keeps the page's relative links valid. List and 
section pages
+ * (home, download, community, ...) have no article.doc and are skipped.
  */
 async function generateMarkdown() {
   const turndownService = createTurndownService();
@@ -27,7 +32,11 @@ async function generateMarkdown() {
 
   // Get all HTML files
   const htmlFiles = glob.sync('public/**/*.html', {
-    ignore: ['public/404.html', 'public/**/index.html'] // Skip error pages 
and index pages
+    ignore: [
+      'public/404.html',
+      'public/blog/**/index.html', // blog posts: their Markdown is to come 
from the source, see #1763
+      'public/releases/**/index.html' // release pages are converted by 
generateAllIndexes below
+    ]
   });
 
   let processedCount = 0;
@@ -43,14 +52,17 @@ async function generateMarkdown() {
     for (const htmlFile of batch) {
       try {
         const htmlContent = fs.readFileSync(htmlFile, 'utf8');
-        const { markdown, repaired } = convertPage(htmlContent, 
turndownService);
+        const articleOnly = htmlFile.endsWith('/index.html');
+        const { markdown, repaired } = convertPage(htmlContent, 
turndownService, { articleOnly });
 
         if (repaired) {
           console.warn(`Repaired malformed HTML in ${htmlFile}, fix the 
mis-nested markup in its source`);
         }
 
         if (markdown === null) {
-          console.warn(`Skipping ${htmlFile}: no main content found`);
+          if (!articleOnly) {
+            console.warn(`Skipping ${htmlFile}: no main content found`);
+          }
           continue;
         }
 
@@ -92,10 +104,12 @@ async function generateMarkdown() {
  *
  * @param {string} htmlContent the full HTML page
  * @param {TurndownService} turndownService configured Turndown instance
+ * @param {{articleOnly?: boolean}} [options] articleOnly: only accept an 
article.doc as the main
+ *   content, so list and section pages that merely have a <main> are not 
converted
  * @returns {{markdown: string|null, repaired: boolean}} the Markdown (null 
when the page has no
  *   main content), and whether the HTML was malformed and had to be repaired 
before parsing
  */
-function convertPage(htmlContent, turndownService) {
+function convertPage(htmlContent, turndownService, { articleOnly = false } = 
{}) {
   // node-html-parser cannot repair mis-nested inline tags (Asciidoctor emits 
them for a `*` inside
   // backticks): it unwraps every unclosed ancestor, article.doc included. Let 
jsdom's HTML5 parser
   // repair such pages the way browsers do; it is much slower, so only 
malformed pages go through it.
@@ -104,17 +118,19 @@ function convertPage(htmlContent, turndownService) {
 
   // Extract only the main article content
   // Try different selectors based on Antora and Hugo structure
-  let mainContent = root.querySelector('article.doc') ||
-                   root.querySelector('main') ||
-                   root.querySelector('.article') ||
-                   root.querySelector('article');
+  let mainContent = root.querySelector('article.doc');
+  if (!mainContent && !articleOnly) {
+    mainContent = root.querySelector('main') ||
+                  root.querySelector('.article') ||
+                  root.querySelector('article');
+  }
 
   if (!mainContent) {
     return { markdown: null, repaired };
   }
 
-  // Remove navigation elements, headers, and footers from the content
-  const elementsToRemove = mainContent.querySelectorAll('nav, header, footer, 
.nav, .navbar, .toolbar');
+  // Remove navigation elements, headers, footers and the embedded table of 
contents from the content
+  const elementsToRemove = mainContent.querySelectorAll('nav, header, footer, 
.nav, .navbar, .toolbar, aside.toc');
   elementsToRemove.forEach(el => el.remove());
 
   // Remove anchor links (they are just UI navigation aids)
diff --git a/llms-txt-template.md b/llms-txt-template.md
index 3c08defc..827fcf27 100644
--- a/llms-txt-template.md
+++ b/llms-txt-template.md
@@ -7,6 +7,10 @@ For example:
 - HTML: 
`https://camel.apache.org/components/next/languages/simple-language.html`
 - Markdown: 
`https://camel.apache.org/components/next/languages/simple-language.md`
 
+Website pages have URLs that end in `/`; append `index.md` to read one as 
Markdown:
+- HTML: `https://camel.apache.org/what-is-apache-camel/`
+- Markdown: `https://camel.apache.org/what-is-apache-camel/index.md`
+
 ## Offline documentation bundles
 
 For agents or environments with no or restricted internet access, versioned 
offline documentation bundles are available as zip archives of all Markdown 
files, one per documented Camel version:
@@ -69,10 +73,12 @@ Current top committers (as of 2026, with affiliation from 
the [team page](https:
 
 The full committer list with organizational affiliations is published at 
https://camel.apache.org/community/team/. For year-by-year commit data and the 
full maintainer history, see [Who Maintains Apache 
Camel](https://camel.apache.org/blog/2026/07/camel-who-maintains/).
 
-## What is Apache Camel
+## Start here
 
-- [What is Apache Camel](https://camel.apache.org/what-is-apache-camel/): 
Introduction for newcomers — what Camel does, why it matters, and how to get 
started.
-- [When to use Apache Camel](https://camel.apache.org/when-to-use/): Common 
use cases, real-world scenarios, when Camel fits, and when alternatives might 
be better.
+- [What is Apache 
Camel](https://camel.apache.org/what-is-apache-camel/index.md): Introduction 
for newcomers — what Camel does, why it matters, and how to get started.
+- [When to use Apache Camel](https://camel.apache.org/when-to-use/index.md): 
Common use cases, real-world scenarios, when Camel fits, and when alternatives 
might be better.
+- [Why Apache Camel](https://camel.apache.org/trust/index.md): Why teams trust 
Apache Camel in production — release cadence, LTS, security track record, 
vendor-neutral governance, bug fix data, dependency maintenance, and AI 
readiness.
+- [Camel DNA](https://camel.apache.org/camel-dna/index.md): The origin story — 
from the first JMS-to-File route in 2007 to the Camel CLI today, and why the 
core idea has not changed.
 - [Getting Started](https://camel.apache.org/manual/getting-started.md): Quick 
start guide for Apache Camel.
 - [User Manual](https://camel.apache.org/manual/index.md): Complete user guide 
and reference documentation.
 - [Architecture 
(CamelContext)](https://camel.apache.org/manual/camelcontext.md): How Camel 
works — routes, components, endpoints, processors, producers, consumers, and 
the DSL.
@@ -486,14 +492,13 @@ For high-quality answers about routing, code generation, 
and troubleshooting, AI
 - [Who Maintains Apache 
Camel](https://camel.apache.org/blog/2026/07/camel-who-maintains/): 
Year-by-year commit data showing who maintains the project — the same core 
team, through multiple acquisitions, contributing 80–95% of all commits every 
year since 2007.
 - [Apache Camel Is Not Afraid of 
AI](https://camel.apache.org/blog/2026/07/camel-not-afraid-of-ai/): The project 
pointed a frontier AI model at 19 years of code and fixed all 165 bugs it found 
— concurrency races, silent data loss, security gaps. AI-assisted code review 
is now a standard part of the development process.
 - [A frontier AI coached a small local model through 
Camel](https://camel.apache.org/blog/2026/09/camel-local-model-benchmark/): 
Measured benchmark of a frontier model (13 of 13 beginner examples) and a 22 GB 
local model on a laptop (0 to 12 of 13 over twenty runs) building Camel routes 
with the MCP server, and the 117 findings — 99 of them wrong for humans too — 
fixed in Camel 4.23: error messages that say what to write, validation at write 
time, catalog samples, a stricter YAML schema.
-- [Why Apache Camel](https://camel.apache.org/trust/): Why teams trust Apache 
Camel in production — release cadence, LTS, security track record, 
vendor-neutral governance, bug fix data, dependency maintenance, and AI 
readiness.
 - [Built to Patch 
Fast](https://camel.apache.org/blog/2026/07/camel-security-advisories-4.21.0/): 
How the project handled 32 CVEs in one release — the timeline, the backport 
process, incomplete fixes re-issued as new CVEs, and 31 public PoC reproducers. 
The best single-page overview of Camel's security response in practice.
 - [Security](https://camel.apache.org/security/): Security advisories and 
vulnerability reports.
 - [Security Model](https://camel.apache.org/manual/security-model.md): Apache 
Camel's built-in security model — route policy, payload validation, and how 
Camel protects against injection and untrusted input.
 - [Generating SBOMs](https://camel.apache.org/manual/sbom.html): How to 
generate Software Bill of Materials for Camel applications — Camel CLI, Spring 
Boot (built-in), and Quarkus. Every release since 4.0.3 ships with signed 
CycloneDX SBOMs.
 - [Migration and Upgrade 
Guides](https://camel.apache.org/manual/migration-and-upgrade.md): Apache Camel 
migration and upgrade guides.
 - [Camel Update Recipes](https://github.com/apache/camel-upgrade-recipes): 
OpenRewrite recipes for automatic Camel application upgrades.
-- [Who uses Apache Camel](https://camel.apache.org/community/user-stories/): 
Companies and projects using Apache Camel in production.
+- [Who uses Apache 
Camel](https://camel.apache.org/community/user-stories/index.md): Companies and 
projects using Apache Camel in production.
 - [Echonect: Fifteen Years on Apache 
Camel](https://camel.apache.org/blog/2026/07/echonect-fifteen-years-apache-camel/):
 Detailed five-part case study of one of Europe's larger SMS gateways — 15 
years on Camel, 500+ messages per second per node, 99.97% uptime, same team, 
same architecture.
 - [Commercial 
Support](https://camel.apache.org/manual/commercial-camel-offerings.md): 
Companies offering commercial Apache Camel support and services.
 - [GitHub](https://github.com/apache/camel): Source code repository.
diff --git a/test/generate-markdown-test.js b/test/generate-markdown-test.js
index e1941714..1a962b9c 100644
--- a/test/generate-markdown-test.js
+++ b/test/generate-markdown-test.js
@@ -88,3 +88,28 @@ test('a page that needed repair is flagged so its source can 
be fixed upstream',
 
   assert.equal(convertPage(html, createTurndownService()).repaired, true)
 })
+
+// Hugo renders website pages as <page>/index.html with the content in 
article.doc (the "static"
+// layouts), and list pages (home, download, community) with only a <main>. 
Only the former have a
+// Markdown mirror, so their embedded table of contents must not leak into the 
Markdown either.
+test('a Hugo content page converts its article without the embedded table of 
contents', () => {
+  const html = `<!DOCTYPE html><html><head><title>t</title></head><body><main>
+<article class="static doc community"><h1>Team</h1>
+<aside class="toc embedded" aria-label="Table of contents"><div 
class="toc-menu"><h3 id="toc-heading">Contents</h3>
+<nav id="TableOfContents"><ul><li><a 
href="#committers">Committers</a></li></ul></nav></div></aside>
+<p>This page lists who we are.</p>
+<h2 id="committers">Committers</h2></article>
+<aside class="toc sidebar"><div 
class="toc-menu"></div></aside></main></body></html>`
+
+  assert.deepEqual(convertPage(html, createTurndownService(), { articleOnly: 
true }), {
+    markdown: '# Team\n\nThis page lists who we are.\n\n## Committers',
+    repaired: false,
+  })
+})
+
+test('a page with only a main is converted by default but skipped when an 
article is required', () => {
+  const html = '<!DOCTYPE 
html><html><head><title>t</title></head><body><main><h1>Downloads</h1><p>Cards.</p></main></body></html>'
+
+  assert.equal(convertPage(html, createTurndownService()).markdown, '# 
Downloads\n\nCards.')
+  assert.deepEqual(convertPage(html, createTurndownService(), { articleOnly: 
true }), { markdown: null, repaired: false })
+})

Reply via email to