Skip to content

Commit

Permalink
[code-infra] Remove commonjs imports in docs (#44976)
Browse files Browse the repository at this point in the history
  • Loading branch information
Janpot authored Jan 21, 2025
1 parent cce1222 commit a56abff
Show file tree
Hide file tree
Showing 28 changed files with 357 additions and 334 deletions.
2 changes: 1 addition & 1 deletion babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -149,7 +149,7 @@ module.exports = function getBabelConfig(api) {
ignore: [/@babel[\\|/]runtime/], // Fix a Windows issue.
overrides: [
{
exclude: /\.test\.(js|ts|tsx)$/,
exclude: /\.test\.(m?js|ts|tsx)$/,
plugins: ['@babel/plugin-transform-react-constant-elements'],
},
{
Expand Down
7 changes: 0 additions & 7 deletions docs/config.d.ts

This file was deleted.

15 changes: 4 additions & 11 deletions docs/config.js → docs/config.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
// Valid languages to server-side render in production
const LANGUAGES = ['en'];
export const LANGUAGES = ['en'];

// Server side rendered languages
const LANGUAGES_SSR = ['en'];
export const LANGUAGES_SSR = ['en'];

// Work in progress
const LANGUAGES_IN_PROGRESS = LANGUAGES.slice();
export const LANGUAGES_IN_PROGRESS = LANGUAGES.slice();

const LANGUAGES_IGNORE_PAGES = (pathname) => {
export const LANGUAGES_IGNORE_PAGES = (pathname: string) => {
// We don't have the bandwidth like Qt to translate our blog posts
// https://www.qt.io/zh-cn/blog
if (pathname === '/blog' || pathname.startsWith('/blog/')) {
Expand All @@ -20,10 +20,3 @@ const LANGUAGES_IGNORE_PAGES = (pathname) => {

return false;
};

module.exports = {
LANGUAGES,
LANGUAGES_IN_PROGRESS,
LANGUAGES_SSR,
LANGUAGES_IGNORE_PAGES,
};
16 changes: 6 additions & 10 deletions docs/next.config.mjs → docs/next.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,18 +5,14 @@ import * as fs from 'fs';
// @ts-ignore
import { BundleAnalyzerPlugin } from 'webpack-bundle-analyzer';
import { createRequire } from 'module';
import { findPages } from './src/modules/utils/find.mjs';
import { NextConfig } from 'next';
import { findPages } from './src/modules/utils/find';
import { LANGUAGES, LANGUAGES_SSR, LANGUAGES_IGNORE_PAGES, LANGUAGES_IN_PROGRESS } from './config';

const currentDirectory = url.fileURLToPath(new URL('.', import.meta.url));
const require = createRequire(import.meta.url);

const withDocsInfra = require('./nextConfigDocsInfra.js');
const {
LANGUAGES,
LANGUAGES_SSR,
LANGUAGES_IGNORE_PAGES,
LANGUAGES_IN_PROGRESS,
} = require('./config.js');
const withDocsInfra = require('./nextConfigDocsInfra');

const workspaceRoot = path.join(currentDirectory, '../');

Expand All @@ -30,7 +26,7 @@ const pkgContent = fs.readFileSync(path.resolve(workspaceRoot, 'package.json'),
const pkg = JSON.parse(pkgContent);

export default withDocsInfra({
webpack: (config, options) => {
webpack: (config: NextConfig, options): NextConfig => {
const plugins = config.plugins.slice();

if (process.env.DOCS_STATS_ENABLED) {
Expand Down Expand Up @@ -276,4 +272,4 @@ export default withDocsInfra({
];
},
}),
});
} satisfies NextConfig);
2 changes: 1 addition & 1 deletion docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
"typescript": "tsc -p tsconfig.json && tsc -p scripts/tsconfig.json",
"typescript:transpile": "echo 'Use `pnpm docs:typescript:formatted'` instead && exit 1",
"typescript:transpile:dev": "echo 'Use `pnpm docs:typescript'` instead && exit 1",
"link-check": "node ./scripts/reportBrokenLinks.js"
"link-check": "tsx ./scripts/reportBrokenLinks.js"
},
"dependencies": {
"@babel/core": "^7.26.0",
Expand Down
2 changes: 1 addition & 1 deletion docs/pages/_app.js
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ import SvgBaseUiLogo, {
} from 'docs/src/icons/SvgBaseUiLogo';
import './global.css';
import '../public/static/components-gallery/base-theme.css';
import config from '../config';
import * as config from '../config';

// Remove the license warning from demonstration purposes
LicenseInfo.setLicenseKey(process.env.NEXT_PUBLIC_MUI_LICENSE);
Expand Down
254 changes: 42 additions & 212 deletions docs/scripts/reportBrokenLinks.js
Original file line number Diff line number Diff line change
@@ -1,182 +1,14 @@
/* eslint-disable no-console */
const path = require('path');
const fse = require('fs-extra');
const { createRender } = require('@mui/internal-markdown');
const { marked } = require('marked');
const { LANGUAGES_IGNORE_PAGES } = require('../config');

// Use renderer to extract all links into a markdown document
function getPageLinks(markdown) {
const hrefs = [];

const renderer = new marked.Renderer();
renderer.link = ({ href }) => {
if (href.startsWith('/')) {
hrefs.push(href);
}
};
marked(markdown, { renderer });
return hrefs;
}

// List all .js files in a folder
function getJsFilesInFolder(folderPath) {
const files = fse.readdirSync(folderPath, { withFileTypes: true });
return files.reduce((acc, file) => {
if (file.isDirectory()) {
const filesInFolder = getJsFilesInFolder(path.join(folderPath, file.name));
return [...acc, ...filesInFolder];
}
if (file.name.endsWith('.js') || file.name.endsWith('.tsx')) {
return [...acc, path.join(folderPath, file.name).replace(/\\/g, '/')];
}
return acc;
}, []);
}

// Returns url assuming it's "./docs/pages/x/..." becomes "mui.com/x/..."
const jsFilePathToUrl = (jsFilePath) => {
const folder = path.dirname(jsFilePath);
const file = path.basename(jsFilePath);

const root = folder.slice(jsFilePath.indexOf('/pages') + '/pages'.length);
const suffix = path.extname(file);
let page = `/${file.slice(0, file.length - suffix.length)}/`;

if (page === '/index/') {
page = '/';
}

return `${root}${page}`;
};

function cleanLink(link) {
const startQueryIndex = link.indexOf('?');
const endQueryIndex = link.indexOf('#', startQueryIndex);

if (startQueryIndex === -1) {
return link;
}
if (endQueryIndex === -1) {
return link.slice(0, startQueryIndex);
}
return `${link.slice(0, startQueryIndex)}${link.slice(endQueryIndex)}`;
}

function getLinksAndAnchors(fileName) {
const headingHashes = {};
const render = createRender({
headingHashes,
options: {
ignoreLanguagePages: LANGUAGES_IGNORE_PAGES,
env: {
SOURCE_CODE_REPO: '',
},
},
});

const data = fse.readFileSync(fileName, { encoding: 'utf8' });
render(data);

const links = getPageLinks(data).map(cleanLink);

return {
hashes: Object.keys(headingHashes),
links,
};
}

const markdownImportRegExp = /'(.*)\?(muiMarkdown|@mui\/markdown)'/g;

const getMdFilesImported = (jsPageFile) => {
// For each JS file extract the markdown rendered if it exists
const fileContent = fse.readFileSync(jsPageFile, 'utf8');
/**
* Content files can be represented by either:
* - 'docsx/data/advanced-components/overview.md?muiMarkdown'; (for mui-x)
* - 'docs/data/advanced-components/overview.md?muiMarkdown';
* - './index.md?muiMarkdown';
*/
const importPaths = fileContent.match(markdownImportRegExp);

if (importPaths === null) {
return [];
}
return importPaths.map((importPath) => {
let cleanImportPath = importPath.replace(markdownImportRegExp, '$1');
if (cleanImportPath.startsWith('.')) {
cleanImportPath = path.join(path.dirname(jsPageFile), cleanImportPath);
} else {
/**
* convert /Users/oliviertassinari/base-ui/docs/pages/base-ui/react-switch/index.js
* and docs-base/data/base/components/switch/switch.md
* into /Users/oliviertassinari/base-ui/docs/data/base/components/switch/switch.md
*/
const cleanImportPathArray = cleanImportPath.split('/');
// Assume that the first folder is /docs or an alias that starts with /docs
cleanImportPathArray.shift();

// Truncate jsPageFile at /docs/ and append cleanImportPath
cleanImportPath = path.join(
jsPageFile.slice(0, jsPageFile.indexOf('/docs/')),
'docs',
cleanImportPathArray.join('/'),
);
}

return cleanImportPath;
});
};

const parseDocFolder = (folderPath, availableLinks = {}, usedLinks = {}) => {
const jsPageFiles = getJsFilesInFolder(folderPath);

const mdFiles = jsPageFiles.flatMap((jsPageFile) => {
const pageUrl = jsFilePathToUrl(jsPageFile);
const importedMds = getMdFilesImported(jsPageFile);

return importedMds.map((fileName) => ({ fileName, url: pageUrl }));
});

// Mark all the existing page as available
jsPageFiles.forEach((jsFilePath) => {
const url = jsFilePathToUrl(jsFilePath);
availableLinks[url] = true;
});

// For each markdown file, extract links
mdFiles.forEach(({ fileName, url }) => {
const { hashes, links } = getLinksAndAnchors(fileName);

links.forEach((link) => {
if (usedLinks[link] === undefined) {
usedLinks[link] = [fileName];
} else {
usedLinks[link].push(fileName);
}
});

hashes.forEach((hash) => {
availableLinks[`${url}#${hash}`] = true;
});
});
};

const getAnchor = (link) => {
const splittedPath = link.split('/');
const potentialAnchor = splittedPath[splittedPath.length - 1];
return potentialAnchor.includes('#') ? potentialAnchor : '';
};

// Export useful method for doing similar checks in other repositories
module.exports = { parseDocFolder, getAnchor };
import path from 'path';
import fse from 'fs-extra';
import { parseDocFolder, getAnchor } from './reportBrokenLinksLib';

/**
* The remaining pat to the code is specific to this repository
*/
const UNSUPPORTED_PATHS = ['/api/', '/careers/', '/store/', '/x/'];

const docsSpaceRoot = path.join(__dirname, '../');
const docsSpaceRoot = path.join(path.dirname(new URL(import.meta.url).pathname), '../');

const buffer = [];
function write(text) {
Expand All @@ -193,43 +25,41 @@ function getPageUrlFromLink(link) {
return rep;
}

if (require.main === module) {
// {[url with hash]: true}
const availableLinks = {};

// {[url with hash]: list of files using this link}
const usedLinks = {};

parseDocFolder(path.join(docsSpaceRoot, './pages/'), availableLinks, usedLinks);

write('Broken links found by `pnpm docs:link-check` that exist:\n');
Object.keys(usedLinks)
.filter((link) => link.startsWith('/'))
.filter((link) => !availableLinks[link])
// these url segments are specific to Base UI and added by scripts (can not be found in markdown)
.filter((link) =>
['components-api', 'hooks-api', '#unstyled'].every((str) => !link.includes(str)),
)
.filter((link) => UNSUPPORTED_PATHS.every((unsupportedPath) => !link.includes(unsupportedPath)))
.sort()
.forEach((linkKey) => {
//
// <!-- #default-branch-switch -->
//
write(`- https://mui.com${linkKey}`);
console.log(`https://mui.com${linkKey}`);

console.log(`used in`);
usedLinks[linkKey].forEach((f) => console.log(`- ${path.relative(docsSpaceRoot, f)}`));
console.log('available anchors on the same page:');
console.log(
Object.keys(availableLinks)
.filter((link) => getPageUrlFromLink(link) === getPageUrlFromLink(linkKey))
.sort()
.map(getAnchor)
.join('\n'),
);
console.log('\n\n');
});
save(buffer);
}
// {[url with hash]: true}
const availableLinks = {};

// {[url with hash]: list of files using this link}
const usedLinks = {};

parseDocFolder(path.join(docsSpaceRoot, './pages/'), availableLinks, usedLinks);

write('Broken links found by `pnpm docs:link-check` that exist:\n');
Object.keys(usedLinks)
.filter((link) => link.startsWith('/'))
.filter((link) => !availableLinks[link])
// these url segments are specific to Base UI and added by scripts (can not be found in markdown)
.filter((link) =>
['components-api', 'hooks-api', '#unstyled'].every((str) => !link.includes(str)),
)
.filter((link) => UNSUPPORTED_PATHS.every((unsupportedPath) => !link.includes(unsupportedPath)))
.sort()
.forEach((linkKey) => {
//
// <!-- #default-branch-switch -->
//
write(`- https://mui.com${linkKey}`);
console.log(`https://mui.com${linkKey}`);

console.log(`used in`);
usedLinks[linkKey].forEach((f) => console.log(`- ${path.relative(docsSpaceRoot, f)}`));
console.log('available anchors on the same page:');
console.log(
Object.keys(availableLinks)
.filter((link) => getPageUrlFromLink(link) === getPageUrlFromLink(linkKey))
.sort()
.map(getAnchor)
.join('\n'),
);
console.log('\n\n');
});
save(buffer);
Loading

0 comments on commit a56abff

Please sign in to comment.