Skip to content
Merged
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
1 change: 1 addition & 0 deletions packages/lexical-website/.gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
.cache-loader
/docs/api
/docs/packages
/static/llms

# Misc
.DS_Store
Expand Down
2 changes: 2 additions & 0 deletions packages/lexical-website/docusaurus.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {fileURLToPath} from 'node:url';
import {themes} from 'prism-react-renderer';

import {packagesManager} from '../../scripts/shared/packagesManager.mjs';
import copyPageButtonPlugin from './plugins/copy-page-button/index.mjs';
import packageDocsPlugin from './plugins/package-docs/index.mjs';
import slugifyPlugin from './src/plugins/lexical-remark-slugify-anchors/index.js';

Expand Down Expand Up @@ -345,6 +346,7 @@ const config: Config = {
},
],
'./plugins/webpack-buffer',
copyPageButtonPlugin,
async function webpackLexicalModules() {
return {
configureWebpack() {
Expand Down
1 change: 1 addition & 0 deletions packages/lexical-website/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"@docusaurus/core": "^3.10.1",
"@docusaurus/faster": "^3.10.1",
"@docusaurus/plugin-client-redirects": "^3.10.1",
"@docusaurus/plugin-content-docs": "^3.10.1",
"@docusaurus/preset-classic": "^3.10.1",
"@docusaurus/theme-common": "^3.10.1",
"@docusaurus/theme-mermaid": "^3.10.1",
Expand Down
110 changes: 110 additions & 0 deletions packages/lexical-website/plugins/copy-page-button/index.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

import fs from 'node:fs';
import path from 'node:path';

import {MARKDOWN_NAMESPACE, relativeMarkdownPath} from './markdownPath.mjs';

const SITE_ALIAS = '@site';

function stripFrontMatter(raw) {
return raw.replace(/^\uFEFF?---\r?\n[\s\S]*?\r?\n---\r?\n?/, '');
}

/**
* Drop the leading block of MDX `import`/`export` statements (and blank lines)
* that appear before the first piece of real content. Only the leading block is
* removed so `import`/`export` lines inside code fences are left untouched.
*/
function stripLeadingMdxStatements(body) {
const lines = body.split('\n');
let index = 0;
for (; index < lines.length; index++) {
const trimmed = lines[index].trim();
if (trimmed === '' || /^(?:import|export)\b/.test(trimmed)) {
continue;
}
break;
}
return lines.slice(index).join('\n');
}

/**
* Emit a clean Markdown copy of every doc page at build time so the
* server-rendered CopyPageButton can link to / copy / hand off real Markdown
* without any client-side DOM scraping.
*
* @type {import('@docusaurus/types').PluginModule}
*/
const copyPageButtonPlugin = async function (context) {
const {siteDir, siteConfig} = context;
const {baseUrl, url: siteUrl} = siteConfig;
const outputRoot = path.join(siteDir, 'static', MARKDOWN_NAMESPACE);

const resolveSource = source => {
if (source.startsWith(SITE_ALIAS)) {
return path.join(siteDir, source.slice(SITE_ALIAS.length));
}
return path.isAbsolute(source) ? source : path.join(siteDir, source);
};

return {
// Runs in both dev and production, after every plugin has loaded its
// content, so we have the authoritative permalink -> source mapping for
// every doc (including the generated API reference).
allContentLoaded({allContent}) {
const docsContent = allContent['docusaurus-plugin-content-docs'];
if (!docsContent) {
return;
}

// Regenerate from scratch so renamed/removed pages don't leave orphans.
fs.rmSync(outputRoot, {force: true, recursive: true});

const normalizedSiteUrl = String(siteUrl || '').replace(/\/$/, '');

for (const instance of Object.values(docsContent)) {
const loadedVersions = (instance && instance.loadedVersions) || [];
for (const version of loadedVersions) {
for (const doc of version.docs || []) {
const sourcePath = resolveSource(doc.source);
let raw;
try {
raw = fs.readFileSync(sourcePath, 'utf-8');
} catch {
continue;
}

let body = stripFrontMatter(raw);
if (sourcePath.endsWith('.mdx')) {
body = stripLeadingMdxStatements(body);
}
body = body.trim();

const pageUrl = `${normalizedSiteUrl}${doc.permalink}`;
const header = /^#\s/.test(body)
? `URL: ${pageUrl}\n\n`
: `# ${doc.title}\n\nURL: ${pageUrl}\n\n`;

const outputPath = path.join(
outputRoot,
`${relativeMarkdownPath(doc.permalink, baseUrl)}.md`,
);
fs.mkdirSync(path.dirname(outputPath), {recursive: true});
fs.writeFileSync(outputPath, `${header}${body}\n`);
}
}
}
},

name: 'copy-page-button',
};
};

export default copyPageButtonPlugin;
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*/

/**
* Path namespace (under `static/`) where the build-time plugin emits a Markdown
* copy of every doc page, e.g. the page `/docs/intro` is served at
* `/llms/docs/intro.md`.
*/
export const MARKDOWN_NAMESPACE = 'llms';

/**
* Map a doc permalink to the namespace-relative path of its generated Markdown
* file (without the `MARKDOWN_NAMESPACE` prefix or surrounding slashes), e.g.
* `/docs/api/` -> `docs/api`.
*
* Shared by the plugin that writes the file and the CopyPageButton that links
* to it so the two normalizations (notably trailing-slash handling for index
* pages) can never drift.
*
* @param {string} permalink Doc permalink, including baseUrl (e.g. `/docs/api/`).
* @param {string} baseUrl Site baseUrl (e.g. `/`).
* @returns {string}
*/
export function relativeMarkdownPath(permalink, baseUrl) {
let rel = permalink;
if (baseUrl && rel.startsWith(baseUrl)) {
rel = rel.slice(baseUrl.length);
}
return rel.replace(/^\/+/, '').replace(/\/+$/, '') || 'index';
}
Loading
Loading