blob: 3c98447fd6e28d3e2b24fde17302958e1e9a3429 [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
// @ts-check
// Note: type annotations allow type checking and IDEs autocompletion
import Ajv from 'ajv';
const {themes} = require('prism-react-renderer');
const lightCodeTheme = themes.github;
const darkCodeTheme = themes.dracula;
/** @type {import('@docusaurus/types').Config} */
const config = {
title: 'Apache Ozone',
tagline: 'Scalable, reliable, distributed storage system optimized for data analytics and object store workloads.',
// Set the production URL of the website. Must be updated when the final site is deployed.
// This must match the URL the website is hosted at for social media previews to work.
// If you are testing the social media image (themeConfig.image) locally, set this to http://localhost:3001.
url: 'https://ozone.apache.org',
// Set the /<baseUrl>/ pathname under which your site is served
// For GitHub pages deployment, it is often '/<projectName>/'
baseUrl: '/',
// Fail the build if there are any broken links.
onBrokenLinks: 'throw',
onBrokenMarkdownLinks: 'throw',
onBrokenAnchors: 'throw',
// Fail the build if multiple pages map to the same URL.
onDuplicateRoutes: 'throw',
// Even if you don't use internalization, you can use this field to set useful
// metadata like html lang. For example, if your site is Chinese, you may want
// to replace "en" with "zh-Hans".
i18n: {
defaultLocale: 'en',
locales: ['en'],
},
/*
Docusaurus does not currently support multiple favicons out of the box.
Manually insert head tags to configure support for favicons on multiple platforms.
*/
headTags: [
{
tagName: 'link',
attributes: {
rel: 'icon',
href: 'favicon.ico',
sizes: '32x32'
},
},
{
tagName: 'link',
attributes: {
rel: 'icon',
href: 'favicon.svg',
type: "image/svg+xml"
},
},
{
tagName: 'link',
attributes: {
rel: 'apple-touch-icon',
href: 'apple-touch-icon.png',
},
},
{
tagName: 'meta',
attributes: {
name: 'google-site-verification',
content: 'fXhAWQ_Jb1fOk6QlN9a7Zs_Xsj-E2U0Q8oFqTNVclaE',
},
},
{
tagName: 'meta',
attributes: {
name: 'algolia-site-verification',
content: 'A2998EF969F36A0D',
},
},
],
markdown: {
mermaid: true,
/*
Validate markdown frontmatter against a more restrictive schema than what Docusaurus allows.
This ensures all pages are using a minimal set of consistent keys.
It can also be used to require all pages to define certain markdown front matter keys.
See https://docusaurus.io/docs/api/docusaurus-config#markdown for reference.
*/
parseFrontMatter: async (params) => {
// Reuse the default parser.
const result = await params.defaultParseFrontMatter(params);
// Validate front matter against the schema.
const schemaPath = './.github/resource/frontmatter.schema.json';
const frontMatterSchema = require(schemaPath);
const ajv = new Ajv();
const validate = ajv.compile(frontMatterSchema);
const isValid = validate(result.frontMatter);
if (!isValid) {
console.error('Front matter validation error in', params.filePath + ':\n', validate.errors);
console.error('Front matter validation failed against JSON schema', schemaPath);
process.exit(1);
}
return result;
},
/*
Validate internal markdown links to ensure they don't contain number prefixes or file extensions.
These can break when the ordering or format of the target page is updated.
Docusaurus can resolve links without these.
See https://docusaurus.io/docs/api/docusaurus-config#markdown for reference.
*/
preprocessor: (/** @type {{filePath: string, fileContent: string}} */ params) => {
const {filePath, fileContent} = params;
// Strip HTML comments and fenced code blocks to avoid false positives.
// Replace non-newline characters with spaces to preserve line numbers.
const contentForValidation = fileContent
.replace(/<!--[\s\S]*?-->/g, (m) => m.replace(/[^\n]/g, ' '))
.replace(/```[\s\S]*?```/g, (m) => m.replace(/[^\n]/g, ' '));
// Match markdown links but exclude images (which start with !)
// Uses negative lookbehind (?<!!) to skip ![alt](url) image syntax
const internalLinkPattern = /(?<!!)\[([^\]]+)\]\(([^()\s]+(?:\([^)]*\)[^()\s]*)*)\)/g;
// Match reference link definitions: [ref]: url
const refLinkDefPattern = /^\[([^\]]+)\]:\s+(\S+)/gm;
// Match two-digit number prefixes at start or after slash
const numberPrefixPattern = /(^|\/)\d{2}-/;
let matches;
const invalidLinks = [];
while ((matches = internalLinkPattern.exec(contentForValidation)) !== null) {
const linkText = matches[1];
// Strip angle brackets (e.g., <./ozone-manager.md> -> ./ozone-manager.md)
const linkPath = matches[2].replace(/^<|>$/g, '');
// Skip external links (http://, https://, mailto:, etc.)
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(linkPath)) {
continue;
}
const pathWithoutFragment = linkPath.split('#')[0];
// Skip absolute paths to pages/static (e.g., /download, /foo.pdf) since they are not versioned
// Only check absolute paths to /docs/ which breaks versioning
const isAbsoluteNonDocsPath = linkPath.startsWith('/') && !linkPath.startsWith('/docs/');
if (isAbsoluteNonDocsPath) {
continue;
}
// Check for absolute paths to docs (breaks versioning)
const isAbsoluteDocsPath = linkPath.startsWith('/docs/');
// Check for number prefixes
const hasNumberPrefix = numberPrefixPattern.test(linkPath);
// Check for extensions that Docusaurus converts to routes (should be omitted in links)
const hasDocExtension = /\.(mdx?|jsx?|tsx)$/.test(pathWithoutFragment);
if (isAbsoluteDocsPath || hasNumberPrefix || hasDocExtension) {
invalidLinks.push({
text: linkText,
path: linkPath,
line: fileContent.substring(0, matches.index).split('\n').length
});
}
}
// Check reference link definitions: [ref]: url
while ((matches = refLinkDefPattern.exec(contentForValidation)) !== null) {
const linkText = matches[1];
const linkPath = matches[2].replace(/^<|>$/g, '');
if (/^[a-zA-Z][a-zA-Z0-9+.-]*:/.test(linkPath)) {
continue;
}
const pathWithoutFragment = linkPath.split('#')[0];
const isAbsoluteNonDocsPath = linkPath.startsWith('/') && !linkPath.startsWith('/docs/');
if (isAbsoluteNonDocsPath) {
continue;
}
const isAbsoluteDocsPath = linkPath.startsWith('/docs/');
const hasNumberPrefix = numberPrefixPattern.test(linkPath);
const hasDocExtension = /\.(mdx?|jsx?|tsx)$/.test(pathWithoutFragment);
if (isAbsoluteDocsPath || hasNumberPrefix || hasDocExtension) {
invalidLinks.push({
text: linkText,
path: linkPath,
line: contentForValidation.substring(0, matches.index).split('\n').length
});
}
}
if (invalidLinks.length > 0) {
const errorMsg = invalidLinks.map(link =>
` Line ${link.line}: [${link.text}](${link.path})`
).join('\n');
console.error('Invalid internal links found in', filePath + ':\n' + errorMsg);
console.error('\nInternal links should not include absolute paths to docs, number prefixes, or route file extensions (.md, .mdx, .js, .jsx, .tsx).');
console.error('Example: use \'./ozone-manager#persisted-state\' instead of \'./02-ozone-manager.md#persisted-state\'');
process.exit(1);
}
return fileContent;
},
},
presets: [
[
'classic',
/** @type {import('@docusaurus/preset-classic').Options} */
({
docs: {
sidebarPath: undefined,
editUrl:
'https://github.com/apache/ozone-site/tree/master',
// TODO: The following sections are currently hidden. Ensure that a section contains a few pages
// of publishable quality before enabling visibility for that section.
exclude: [
'**/06-troubleshooting/**',
]
},
blog: {
showReadingTime: true,
editUrl:
'https://github.com/apache/ozone-site/tree/master/',
},
theme: {
customCss: [
require.resolve('./src/css/custom.css'),
require.resolve('./src/css/header.css'),
require.resolve('./src/css/footer.css'),
],
},
sitemap: {
/*
Check that all generated URLs from the build use kebab-case and lowercase.
See https://docusaurus.io/docs/api/plugins/@docusaurus/plugin-sitemap#ex-config for reference.
*/
createSitemapItems: async (params) => {
const {defaultCreateSitemapItems, ...rest} = params;
const items = await defaultCreateSitemapItems(rest);
const validUrlRegex = /^https:\/\/ozone\.apache\.org\/([a-z0-9][a-z0-9./-]*[a-z0-9/])?$/;
items.forEach((item) => {
if (!validUrlRegex.test(item.url)) {
console.error('Generated URL', item.url, 'does not match the allowed RegEx:', validUrlRegex);
console.error('All URLs should use kebab case and lowercase letters.');
process.exit(1);
}
});
return items;
},
},
}),
],
],
plugins: [
[
'@docusaurus/plugin-pwa',
{
pwaHead: [
{
tagName: 'link',
rel: 'manifest',
href: 'pwa/manifest.json',
},
],
},
]
],
themes: ['@docusaurus/theme-mermaid'],
themeConfig:
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
({
colorMode: {
defaultMode: 'light',
respectPrefersColorScheme: true, // Automatically use dark mode when the user's system prefers it
},
// Optional: Add an announcement bar to the top of the website.
// announcementBar: {
// id: 'announce',
// backgroundColor: 'var(--color-accent)',
// textColor: 'white',
// content:
// 'Sample Announcement Content',
// },
image: 'img/social-card.png',
navbar: {
title: 'Apache Ozone',
logo: {
alt: 'Ozone Logo',
src: 'img/ozone-logo.svg',
},
items: [
{
label: 'Docs',
position: 'left',
items: [
{
label: 'Latest Docs',
to: 'docs',
},
{
label: 'Docs 2.0.0 (Archived)',
href: '/docs/2.0.0/',
target: '_blank'
},
{
label: 'Docs 1.4.1 (Archived)',
href: '/docs/1.4.1/',
target: '_blank'
},
],
},
{
to: 'download',
label: 'Download',
},
{
to: 'roadmap',
label: 'Roadmap',
},
// TODO: The FAQ section is currently hidden. Ensure that the FAQ page
// is of publishable quality before enabling visibility for this section.
// {
// to: 'faq',
// label: 'FAQ',
// },
{
to: '/blog',
label: 'Blog',
},
{
label: 'Community',
items: [
{
to: 'community/communication-channels',
label: 'Communication Channels',
},
{
to: 'community/who-uses-ozone',
label: 'Who Uses Ozone?',
},
{
to: 'community/report-an-issue',
label: 'Report An Issue',
},
{
to: 'community/ask-a-question',
label: 'Ask a Question',
},
{
to: 'community/how-to-contribute',
label: 'How to Contribute',
},
{
to: 'community/events-and-media',
label: 'Events and Media',
},
]
},
// TODO: Enable if multiple languages are supported. See https://issues.apache.org/jira/browse/HDDS-9571
// {
// type: 'localeDropdown',
// position: 'right',
// },
{
href: 'https://github.com/apache/ozone',
position: 'right',
className: 'header-github-link',
'aria-label': 'GitHub Repo',
},
],
},
footer: {
links: [
{
title: 'Apache Software Foundation',
items: [
{
label: 'Foundation',
href: 'https://www.apache.org/'
},
{
label: 'License',
href: 'https://www.apache.org/licenses/'
},
{
label: 'Events',
href: 'https://www.apache.org/events/current-event'
},
{
label: 'Sponsorship',
href: 'https://www.apache.org/foundation/sponsorship.html'
},
{
label: 'Privacy',
href: 'https://privacy.apache.org/policies/privacy-policy-public.html'
},
{
label: 'Security',
href: 'https://www.apache.org/security/'
},
{
label: 'Thanks',
href: 'https://www.apache.org/foundation/thanks.html'
},
]
},
{
title: 'Community',
items: [
{
label: 'GitHub Discussions',
href: 'https://github.com/apache/ozone/discussions'
},
{
label: 'Jira Issues',
href: 'https://issues.apache.org/jira/projects/HDDS/issues'
},
{
label: 'Slack',
href: 'https://infra.apache.org/slack.html'
},
{
label: 'Mailing List',
href: 'mailto:dev@ozone.apache.org'
},
{
label: 'YouTube',
href: 'https://www.youtube.com/@ApacheOzone'
},
{
label: 'Twitter',
href: 'https://twitter.com/ApacheOzone'
},
],
},
{
title: 'Repositories',
items: [
{
label: 'Ozone',
to: 'https://github.com/apache/ozone',
},
{
label: 'Website',
to: 'https://github.com/apache/ozone-site',
},
{
label: 'Docker Image',
to: 'https://github.com/apache/ozone-docker',
},
{
label: 'Docker Runner Image',
to: 'https://github.com/apache/ozone-docker-runner',
},
],
},
],
copyright: `
<div>
Copyright © ${new Date().getFullYear()} <a href="https://www.apache.org/" class=copyright-link>The Apache Software Foundation</a>. Licensed under the <a href="https://www.apache.org/licenses/LICENSE-2.0" class=copyright-link>Apache License, Version 2.0</a>. <br>
<div>
<p>The Apache Software Foundation, Apache Ozone, Ozone, Apache, the Apache Feather, and the Apache Ozone project logo are either registered trademarks or trademarks of the Apache Software Foundation.</p>
</div>
</div>`,
},
prism: {
theme: lightCodeTheme,
darkTheme: darkCodeTheme,
additionalLanguages: ['bash'],
},
algolia: {
appId: "YQWKI4BIJ7",
apiKey: "47cd671112fb5e0363a4d9724beeb9d4",
indexName: "Apache Ozone website",
searchParameters: {}
}
}),
scripts: ['/script/matomo.js'],
};
module.exports = config;