Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
83 changes: 83 additions & 0 deletions website/docusaurus.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,20 @@ const webpack = require('webpack');
const {getDocusaurusConfig} = require('@vis.gl/docusaurus-website');
const {resolve} = require('path');

const SITE_URL = 'https://visgl.github.io/react-map-gl';

const sharedDocPatterns = [
'README.md',
'whats-new.md',
'upgrade-guide.md',
'contributing.md',
'get-started/**'
];
const mapboxApiPatterns = ['api-reference/mapbox/**'];
const maplibreApiPatterns = ['api-reference/maplibre/**'];
const mapboxDocPatterns = [...sharedDocPatterns, ...mapboxApiPatterns];
const maplibreDocPatterns = [...sharedDocPatterns, ...maplibreApiPatterns];

const config = getDocusaurusConfig({
projectName: 'react-map-gl',
tagline: 'React components for Mapbox GL JS and Maplibre GL JS',
Expand Down Expand Up @@ -39,6 +53,75 @@ const config = getDocusaurusConfig({
'./src/styles.css',
'./src/mapbox-gl.css',
'./src/maplibre-gl.css'
],

plugins: [
[
'docusaurus-plugin-llms',
{
docsDir: [{path: '../docs', routeBasePath: 'docs', label: 'Docs'}],
generateLLMsTxt: false,
generateLLMsFullTxt: false,
generateMarkdownFiles: false,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Index links point to missing markdown

Medium Severity

generateMarkdownFiles is set to false, but docusaurus-plugin-llms v0.4.0 still appends .md to doc links in the generated index files. Those URLs do not exist in the static build (live pages are HTML paths without .md), so agents following llms.txt / llms-mapbox.txt / llms-maplibre.txt links get 404s unless they only use the full-content bundles.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit 13514f5. Configure here.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This seems to be true - the index links end in .md, while the deployed Docusaurus routes generally do not.

excludeImports: true,
removeDuplicateHeadings: true,
title: 'react-map-gl',
description: 'React components for Mapbox GL JS and MapLibre GL JS',
customLLMFiles: [
{
filename: 'llms.txt',
fullContent: false,
title: 'react-map-gl',
description: 'React components for Mapbox GL JS and MapLibre GL JS',
includePatterns: sharedDocPatterns,
orderPatterns: sharedDocPatterns,
includeUnmatchedLast: false,
rootContent: `Choose the documentation bundle for your base map library:

- [Mapbox GL JS index](${SITE_URL}/llms-mapbox.txt) (full content: [llms-mapbox-full.txt](${SITE_URL}/llms-mapbox-full.txt))
- [MapLibre GL JS index](${SITE_URL}/llms-maplibre.txt) (full content: [llms-maplibre-full.txt](${SITE_URL}/llms-maplibre-full.txt))

Only load one stack's files — API reference pages are parallel but not interchangeable.`
},
{
filename: 'llms-mapbox.txt',
fullContent: false,
title: 'react-map-gl (Mapbox GL JS)',
description: 'Docs for react-map-gl with Mapbox GL JS',
includePatterns: mapboxDocPatterns,
orderPatterns: mapboxDocPatterns,
includeUnmatchedLast: false
},
{
filename: 'llms-mapbox-full.txt',
fullContent: true,
title: 'react-map-gl (Mapbox GL JS) — full',
description: 'Docs for react-map-gl with Mapbox GL JS',
includePatterns: mapboxDocPatterns,
orderPatterns: mapboxDocPatterns,
includeUnmatchedLast: false
},
{
filename: 'llms-maplibre.txt',
fullContent: false,
title: 'react-map-gl (MapLibre GL JS)',
description: 'Docs for react-map-gl with MapLibre GL JS',
includePatterns: maplibreDocPatterns,
orderPatterns: maplibreDocPatterns,
includeUnmatchedLast: false
},
{
filename: 'llms-maplibre-full.txt',
fullContent: true,
title: 'react-map-gl (MapLibre GL JS) — full',
description: 'Docs for react-map-gl with MapLibre GL JS',
includePatterns: maplibreDocPatterns,
orderPatterns: maplibreDocPatterns,
includeUnmatchedLast: false
}
]
}
]
]
});

Expand Down
7 changes: 5 additions & 2 deletions website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"description": "Website for vis.gl project",
"scripts": {
"start": "docusaurus start",
"build": "docusaurus build",
"build": "docusaurus build && node scripts/append-examples-to-llms.js",
"clear": "docusaurus clear",
"serve": "docusaurus serve",
"write-heading-ids": "ocular-doc-headers ../docs",
Expand All @@ -22,9 +22,9 @@
"@mapbox/mapbox-gl-draw": "^1.3.0",
"@mapbox/mapbox-gl-geocoder": "^4.7.4",
"@maplibre/maplibre-gl-geocoder": "^1.5.0",
"@vis.gl/docusaurus-website": "1.0.0-alpha.23",
"@turf/area": "^6.5.0",
"@turf/bbox": "^6.5.0",
"@vis.gl/docusaurus-website": "1.0.0-alpha.23",
"d3-array": "^3.1.1",
"d3-scale": "^4.0.2",
"immutable": "^4.0.0",
Expand All @@ -40,5 +40,8 @@
"volta": {
"node": "24.18.0",
"yarn": "1.22.22"
},
"devDependencies": {
"docusaurus-plugin-llms": "^0.4.0"
}
}
177 changes: 177 additions & 0 deletions website/scripts/append-examples-to-llms.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,177 @@
const fs = require('fs');
const path = require('path');

const SITE_URL = 'https://visgl.github.io/react-map-gl';
const BUILD_DIR = path.join(__dirname, '../build');
const REPO_ROOT = path.join(__dirname, '../..');
const EXAMPLES_TOC = path.join(__dirname, '../src/examples/table-of-contents.json');

function getExampleIdsByStack(toc) {
const stacks = {mapbox: [], maplibre: []};

for (const entry of toc) {
if (entry.type !== 'category') {
continue;
}
const label = entry.label.toLowerCase();
if (label === 'mapbox') {
stacks.mapbox = entry.items;
} else if (label === 'maplibre') {
stacks.maplibre = entry.items;
}
}

return stacks;
}

function readTitle(exampleId) {
const mdxPath = path.join(__dirname, '../src/examples', `${exampleId}.mdx`);
if (fs.existsSync(mdxPath)) {
const firstLine = fs.readFileSync(mdxPath, 'utf8').split('\n')[0];
const match = firstLine.match(/^#\s+(.+)$/);
if (match) {
return match[1].trim();
}
}

const readmePath = path.join(REPO_ROOT, 'examples', exampleId, 'README.md');
if (fs.existsSync(readmePath)) {
const firstLine = fs.readFileSync(readmePath, 'utf8').split('\n')[0];
const match = firstLine.match(/^#\s+Example:\s*(.+)$/);
if (match) {
return match[1].trim();
}
}

const slug = exampleId.split('/').pop();
return slug
.split('-')
.map(word => word.charAt(0).toUpperCase() + word.slice(1))
.join(' ');
}

function readBlurb(exampleId) {
const readmePath = path.join(REPO_ROOT, 'examples', exampleId, 'README.md');
if (!fs.existsSync(readmePath)) {
return null;
}

const content = fs.readFileSync(readmePath, 'utf8');
const showcaseMatch = content.match(/This example showcases how to ([^\n.]+(?:\.[^\n.]+)*)\./i);
if (showcaseMatch) {
return showcaseMatch[1].trim().replace(/\.$/, '');
}

return null;
}

function readReadmeIntro(exampleId) {
const readmePath = path.join(REPO_ROOT, 'examples', exampleId, 'README.md');
if (!fs.existsSync(readmePath)) {
return null;
}

const lines = fs.readFileSync(readmePath, 'utf8').split('\n');
const intro = [];

for (let i = 1; i < lines.length; i++) {
const line = lines[i].trim();
if (line.startsWith('## Usage')) {
break;
}
if (line) {
intro.push(line);
}
}

return intro.join('\n').trim() || null;
}

function buildExampleLinks(exampleIds) {
return exampleIds.map(exampleId => {
const title = readTitle(exampleId);
const blurb = readBlurb(exampleId);
const url = `${SITE_URL}/examples/${exampleId}`;
const description = blurb ? `: ${blurb}` : '';
return `- [${title}](${url})${description}`;
});
}

function buildExampleSections(exampleIds) {
const sections = [];

for (const exampleId of exampleIds) {
const title = readTitle(exampleId);
const url = `${SITE_URL}/examples/${exampleId}`;
const intro = readReadmeIntro(exampleId);
const blurb = readBlurb(exampleId);

sections.push(`### ${title}\n\n[View example](${url})`);
if (intro) {
sections.push(`\n${intro}`);
} else if (blurb) {
sections.push(`\nThis example showcases how to ${blurb}.`);
}
sections.push('');
}

return sections.join('\n');
}

function appendSection(filePath, section) {
if (!fs.existsSync(filePath)) {
console.warn(`Skipping ${path.basename(filePath)} — file not found`);
return;
}

const content = fs.readFileSync(filePath, 'utf8').trimEnd();
fs.writeFileSync(filePath, `${content}\n\n${section}\n`);
}

function fixDocUrls(content) {
return content
.replace(
/https:\/\/visgl\.github\.io\/docs\/\.\.\/docs\/README\.md/g,
`${SITE_URL}/docs`
)
.replace(
/https:\/\/visgl\.github\.io\/react-map-gl\/docs\/get-started\/get-started\.md/g,
`${SITE_URL}/docs/get-started.md`
);
}

function fixUrlsInLlmsFiles() {
const llmsFiles = fs
.readdirSync(BUILD_DIR)
.filter(name => name.startsWith('llms') && name.endsWith('.txt'))
.map(name => path.join(BUILD_DIR, name));

for (const filePath of llmsFiles) {
const fixed = fixDocUrls(fs.readFileSync(filePath, 'utf8'));
fs.writeFileSync(filePath, fixed);
}
}

function main() {
const toc = JSON.parse(fs.readFileSync(EXAMPLES_TOC, 'utf8'));
const stacks = getExampleIdsByStack(toc);

for (const [stack, exampleIds] of Object.entries(stacks)) {
if (exampleIds.length === 0) {
continue;
}

const linksSection = `## Examples\n\n${buildExampleLinks(exampleIds).join('\n')}`;
appendSection(path.join(BUILD_DIR, `llms-${stack}.txt`), linksSection);

const fullSection = `## Examples\n\n${buildExampleSections(exampleIds)}`;
appendSection(path.join(BUILD_DIR, `llms-${stack}-full.txt`), fullSection);

console.log(`Appended ${exampleIds.length} ${stack} examples to llms-${stack}.txt`);
}

fixUrlsInLlmsFiles();
console.log('Fixed doc URLs in generated llms files');
}

main();
28 changes: 28 additions & 0 deletions website/yarn.lock
Original file line number Diff line number Diff line change
Expand Up @@ -3483,6 +3483,13 @@ brace-expansion@^1.1.7:
balanced-match "^1.0.0"
concat-map "0.0.1"

brace-expansion@^2.0.2:
version "2.1.1"
resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-2.1.1.tgz#c68b1c4111c76aae3a6fba55d496cee10c39dad8"
integrity sha512-WR1cURNjuvBLMZBMbqM0UoE+WAfdUcEV1ccD8PVBVOI+Z3ND4+SZbN8RsfT2bMuG1qwz5RFvPukSZm5fF2D5eA==
dependencies:
balanced-match "^1.0.0"

braces@^3.0.3, braces@~3.0.2:
version "3.0.3"
resolved "https://registry.yarnpkg.com/braces/-/braces-3.0.3.tgz#490332f40919452272d55a8480adc0c441358789"
Expand Down Expand Up @@ -4438,6 +4445,15 @@ dns-packet@^5.2.2:
dependencies:
"@leichtgewicht/ip-codec" "^2.0.1"

docusaurus-plugin-llms@^0.4.0:
version "0.4.0"
resolved "https://registry.yarnpkg.com/docusaurus-plugin-llms/-/docusaurus-plugin-llms-0.4.0.tgz#020f00d83459c7feb71c7b6e61774d864336d8ae"
integrity sha512-jYlj2HJ5+gu7oJZuJ83Hk8KlB65YlZZ/7UpHXiL7Qr+qpNBkVocmt2Molc6F3HNr5RqcfhWD/98CvgyNztg/ow==
dependencies:
gray-matter "^4.0.3"
minimatch "^9.0.3"
yaml "^2.8.1"

dom-converter@^0.2.0:
version "0.2.0"
resolved "https://registry.yarnpkg.com/dom-converter/-/dom-converter-0.2.0.tgz#6721a9daee2e293682955b6afe416771627bb768"
Expand Down Expand Up @@ -7192,6 +7208,13 @@ minimatch@3.1.2, minimatch@^3.0.4, minimatch@^3.0.5, minimatch@^3.1.1:
dependencies:
brace-expansion "^1.1.7"

minimatch@^9.0.3:
version "9.0.9"
resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-9.0.9.tgz#9b0cb9fcb78087f6fd7eababe2511c4d3d60574e"
integrity sha512-OBwBN9AL4dqmETlpS2zasx+vTeWclWzkblfZk7KTA5j3jeOONz/tRCnZomUyvNg83wL5Zv9Ss6HMJXAgL8R2Yg==
dependencies:
brace-expansion "^2.0.2"

minimist-options@4.1.0:
version "4.1.0"
resolved "https://registry.yarnpkg.com/minimist-options/-/minimist-options-4.1.0.tgz#c0655713c53a8a2ebd77ffa247d342c40f010619"
Expand Down Expand Up @@ -10205,6 +10228,11 @@ yaml@^1.7.2:
resolved "https://registry.yarnpkg.com/yaml/-/yaml-1.10.2.tgz#2301c5ffbf12b467de8da2333a459e29e7920e4b"
integrity sha512-r3vXyErRCYJ7wg28yvBY5VSoAF8ZvlcW9/BwUzEtUsjvX/DKs24dIkuwjtuprwJJHsbyUbLApepYTR1BN4uHrg==

yaml@^2.8.1:
version "2.9.0"
resolved "https://registry.yarnpkg.com/yaml/-/yaml-2.9.0.tgz#78274afd93598a1dfdd6130df6a566defcbf9aa4"
integrity sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==

yargs-parser@^20.2.3:
version "20.2.9"
resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-20.2.9.tgz#2eb7dc3b0289718fc295f362753845c41a0c94ee"
Expand Down
Loading