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
5 changes: 4 additions & 1 deletion .prettierignore
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,7 @@
src/pages/**/*.mdx
src/pages/**/*.md

examples/**/README.md
examples/**/README.md

# Fixture files should not be formatted by Prettier
data/onPostBuild/__fixtures__/*.mdx
14 changes: 9 additions & 5 deletions data/createPages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ import { createContentMenuDataFromPage } from './createContentMenuDataFromPage';
import { DEFAULT_LANGUAGE } from './constants';
import { writeRedirectToConfigFile, getRedirectCount } from './writeRedirectToConfigFile';
import { siteMetadata } from '../../gatsby-config';
import { GatsbyNode, Reporter } from 'gatsby';
Copy link
Member Author

Choose a reason for hiding this comment

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

This wasn't used

import { GatsbyNode } from 'gatsby';
import { examples, DEFAULT_EXAMPLE_LANGUAGES } from '../../src/data/examples/';
import { Example } from '../../src/data/examples/types';

Expand Down Expand Up @@ -252,7 +252,9 @@ export const createPages: GatsbyNode['createPages'] = async ({
// with nginx redirects
writeRedirect(redirectFrom, pagePath);
} else {
reporter.info(`[REDIRECTS] Skipping hash fragment redirect: ${redirectFrom} (hash: ${redirectFromUrl.hash})`);
reporter.info(
`[REDIRECTS] Skipping hash fragment redirect: ${redirectFrom} (hash: ${redirectFromUrl.hash})`,
);
}

createRedirect({
Expand All @@ -276,7 +278,7 @@ export const createPages: GatsbyNode['createPages'] = async ({
contentOrderedList,
contentMenu: contentMenuObject,
script,
layout: { leftSidebar: true, rightSidebar: true, searchBar: true, template: 'base' },
Copy link
Member Author

Choose a reason for hiding this comment

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

searchBar no longer used

layout: { leftSidebar: true, rightSidebar: true, template: 'base' },
},
});
return slug;
Expand Down Expand Up @@ -331,7 +333,7 @@ export const createPages: GatsbyNode['createPages'] = async ({
component: examplesTemplate,
context: {
example,
layout: { sidebar: false, searchBar: false, template: 'examples' },
layout: { sidebar: false, template: 'examples' },
},
});
};
Expand All @@ -350,7 +352,9 @@ export const createPages: GatsbyNode['createPages'] = async ({
// with nginx redirects
writeRedirect(redirectFrom, toPath);
} else {
reporter.info(`[REDIRECTS] Skipping MDX hash fragment redirect: ${redirectFrom} (hash: ${redirectFromUrl.hash})`);
reporter.info(
`[REDIRECTS] Skipping MDX hash fragment redirect: ${redirectFrom} (hash: ${redirectFromUrl.hash})`,
);
}

createRedirect({
Expand Down
16 changes: 6 additions & 10 deletions data/onCreatePage.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,24 @@ import fs from 'fs';
export type LayoutOptions = {
leftSidebar: boolean;
rightSidebar: boolean;
searchBar: boolean;
template: string;
mdx: boolean;
};

const mdxWrapper = path.resolve('src/components/Layout/MDXWrapper.tsx');

const pageLayoutOptions: Record<string, LayoutOptions> = {
'/docs': { leftSidebar: true, rightSidebar: false, searchBar: true, template: 'index', mdx: false },
'/docs': { leftSidebar: true, rightSidebar: false, template: 'index', mdx: false },
'/docs/api/control-api': {
leftSidebar: false,
rightSidebar: false,
searchBar: true,
template: 'control-api',
mdx: false,
},
'/docs/sdks': { leftSidebar: false, rightSidebar: false, searchBar: true, template: 'sdk', mdx: false },
'/examples': { leftSidebar: false, rightSidebar: false, searchBar: true, template: 'examples', mdx: false },
'/docs/how-to/pub-sub': { leftSidebar: true, rightSidebar: true, searchBar: true, template: 'how-to', mdx: true },
'/docs/404': { leftSidebar: false, rightSidebar: false, searchBar: false, template: '404', mdx: false },
'/docs/sdks': { leftSidebar: false, rightSidebar: false, template: 'sdk', mdx: false },
'/examples': { leftSidebar: false, rightSidebar: false, template: 'examples', mdx: false },
'/docs/how-to/pub-sub': { leftSidebar: true, rightSidebar: true, template: 'how-to', mdx: true },
'/docs/404': { leftSidebar: false, rightSidebar: false, template: '404', mdx: false },
};

// Function to extract code element classes from an MDX file
Expand Down Expand Up @@ -66,9 +64,7 @@ export const onCreatePage: GatsbyNode['onCreatePage'] = async ({ page, actions }
...page,
context: {
...page.context,
layout: pathOptions
? pathOptions[1]
: { leftSidebar: true, rightSidebar: true, searchBar: true, template: 'base', mdx: isMDX },
layout: pathOptions ? pathOptions[1] : { leftSidebar: true, rightSidebar: true, template: 'base', mdx: isMDX },
...(isMDX ? { languages: Array.from(detectedLanguages) } : {}),
},
component: isMDX ? `${mdxWrapper}?__contentFilePath=${page.component}` : page.component,
Expand Down
3 changes: 2 additions & 1 deletion data/onPostBuild/__fixtures__/input.mdx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
---
title: Test Fixture
intro: "This is a test introduction"
meta_description: "This is a test description"
redirect_from:
- /old-path
Expand Down Expand Up @@ -67,4 +68,4 @@ Here's a code block with anchors and scripts that should be preserved:

<Aside data-type='note'>
This component should be preserved as-is.
</Aside>
</Aside>
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
exports[`MDX to Markdown Transpilation Full transformation with fixture should transform comprehensive fixture correctly 1`] = `
"# Test Fixture

This is a test introduction




Expand Down Expand Up @@ -50,6 +52,5 @@ Here's a code block with anchors and scripts that should be preserved:

<Aside data-type='note'>
This component should be preserved as-is.
</Aside>
"
</Aside>"
`;
13 changes: 12 additions & 1 deletion data/onPostBuild/transpileMdxToMarkdown.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,10 @@ describe('MDX to Markdown Transpilation', () => {
const inputPath = path.join(__dirname, '__fixtures__', 'input.mdx');
const input = fs.readFileSync(inputPath, 'utf-8');

const { content, title } = transformMdxToMarkdown(input, siteUrl);
const { content, title, intro } = transformMdxToMarkdown(input, siteUrl);

expect(title).toBe('Test Fixture');
expect(intro).toBe('This is a test introduction');
expect(content).toMatchSnapshot();
});

Expand All @@ -37,6 +38,16 @@ Content without title`;
transformMdxToMarkdown(input, siteUrl);
}).toThrow('Missing title in frontmatter');
});

it('should not include intro or throw when it is not present', () => {
const input = `---
title: Test Fixture
---

Content without intro`;

expect(() => transformMdxToMarkdown(input, siteUrl)).not.toThrow();
});
});

describe('removeImportExportStatements', () => {
Expand Down
113 changes: 53 additions & 60 deletions data/onPostBuild/transpileMdxToMarkdown.ts
Original file line number Diff line number Diff line change
Expand Up @@ -214,28 +214,30 @@ function convertImagePathsToGitHub(content: string): string {
const githubBaseUrl = 'https://raw.githubusercontent.com/ably/docs/main/src';
const imageExtensions = '(?:png|jpg|jpeg|gif|svg|webp|bmp|ico)';

return content
// Handle relative paths: ../../../images/...{ext}
.replace(
new RegExp(`!\\[([^\\]]*)\\]\\(((?:\\.\\.\\/)+)(images\\/[^)]+\\.${imageExtensions})\\)`, 'gi'),
(match, altText, relativePath, imagePath) => {
return `![${altText}](${githubBaseUrl}/${imagePath})`;
}
)
// Handle absolute paths: /images/...{ext}
.replace(
new RegExp(`!\\[([^\\]]*)\\]\\(\\/(images\\/[^)]+\\.${imageExtensions})\\)`, 'gi'),
(match, altText, imagePath) => {
return `![${altText}](${githubBaseUrl}/${imagePath})`;
}
)
// Handle direct paths: images/...{ext} (no prefix)
.replace(
new RegExp(`!\\[([^\\]]*)\\]\\((images\\/[^)]+\\.${imageExtensions})\\)`, 'gi'),
(match, altText, imagePath) => {
return `![${altText}](${githubBaseUrl}/${imagePath})`;
}
);
return (
content
// Handle relative paths: ../../../images/...{ext}
.replace(
new RegExp(`!\\[([^\\]]*)\\]\\(((?:\\.\\.\\/)+)(images\\/[^)]+\\.${imageExtensions})\\)`, 'gi'),
(match, altText, relativePath, imagePath) => {
return `![${altText}](${githubBaseUrl}/${imagePath})`;
},
)
// Handle absolute paths: /images/...{ext}
.replace(
new RegExp(`!\\[([^\\]]*)\\]\\(\\/(images\\/[^)]+\\.${imageExtensions})\\)`, 'gi'),
(match, altText, imagePath) => {
return `![${altText}](${githubBaseUrl}/${imagePath})`;
},
)
// Handle direct paths: images/...{ext} (no prefix)
.replace(
new RegExp(`!\\[([^\\]]*)\\]\\((images\\/[^)]+\\.${imageExtensions})\\)`, 'gi'),
(match, altText, imagePath) => {
return `![${altText}](${githubBaseUrl}/${imagePath})`;
},
)
);
}

/**
Expand All @@ -248,37 +250,32 @@ function convertRelativeUrls(content: string, siteUrl: string): string {

// Match markdown links: [text](url)
// Only convert URLs that start with / (relative) and are not external URLs or hash-only
return content.replace(
/\[([^\]]+)\]\(([^)]+)\)/g,
(match, linkText, url) => {
// Don't convert external URLs
if (url.startsWith('http://') || url.startsWith('https://')) {
return match;
}

// Don't convert hash-only anchors
if (url.startsWith('#')) {
return match;
}

// Convert relative URLs (starting with /)
if (url.startsWith('/')) {
return `[${linkText}](${baseUrl}${url})`;
}
return content.replace(/\[([^\]]+)\]\(([^)]+)\)/g, (match, linkText, url) => {
// Don't convert external URLs
if (url.startsWith('http://') || url.startsWith('https://')) {
return match;
}

// Keep other URLs as-is (relative paths without leading /)
// Don't convert hash-only anchors
if (url.startsWith('#')) {
return match;
}
);

// Convert relative URLs (starting with /)
if (url.startsWith('/')) {
return `[${linkText}](${baseUrl}${url})`;
}

// Keep other URLs as-is (relative paths without leading /)
return match;
});
}

/**
* Replace template variables with readable placeholders
*/
function replaceTemplateVariables(content: string): string {
return content
.replace(/{{API_KEY}}/g, 'your-api-key')
.replace(/{{RANDOM_CHANNEL_NAME}}/g, 'your-channel-name');
return content.replace(/{{API_KEY}}/g, 'your-api-key').replace(/{{RANDOM_CHANNEL_NAME}}/g, 'your-channel-name');
}

/**
Expand Down Expand Up @@ -309,7 +306,10 @@ function calculateOutputPath(relativeDirectory: string, fileName: string): strin
/**
* Transform MDX content to clean Markdown
*/
function transformMdxToMarkdown(sourceContent: string, siteUrl: string): { content: string; title: string } {
function transformMdxToMarkdown(
sourceContent: string,
siteUrl: string,
): { content: string; title: string; intro?: string } {
// Stage 1: Parse frontmatter
const parsed = frontMatter<FrontMatterAttributes>(sourceContent);

Expand All @@ -318,6 +318,7 @@ function transformMdxToMarkdown(sourceContent: string, siteUrl: string): { conte
}

const title = parsed.attributes.title;
const intro = parsed.attributes.intro;
let content = parsed.body;

// Stage 2: Remove import/export statements
Expand All @@ -342,9 +343,9 @@ function transformMdxToMarkdown(sourceContent: string, siteUrl: string): { conte
content = replaceTemplateVariables(content);

// Stage 9: Prepend title as markdown heading
const finalContent = `# ${title}\n\n${content}`;
const finalContent = `# ${title}\n\n${intro ? `${intro}\n\n` : ''}${content}`;
Copy link
Member Author

Choose a reason for hiding this comment

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

All other changes here are prettier - this is the key change.


return { content: finalContent, title };
return { content: finalContent, title, intro };
}

/**
Expand Down Expand Up @@ -404,9 +405,7 @@ export const onPostBuild: GatsbyNode['onPostBuild'] = async ({ graphql, reporter
const { data, errors } = await graphql<MdxQueryResult>(query);

if (errors) {
reporter.panicOnBuild(
`${REPORTER_PREFIX} Error running GraphQL query: ${JSON.stringify(errors)}`
);
reporter.panicOnBuild(`${REPORTER_PREFIX} Error running GraphQL query: ${JSON.stringify(errors)}`);
return;
}

Expand All @@ -420,7 +419,7 @@ export const onPostBuild: GatsbyNode['onPostBuild'] = async ({ graphql, reporter

if (!siteUrl) {
reporter.panicOnBuild(
`${REPORTER_PREFIX} siteUrl is not configured in siteMetadata. Please check gatsby-config.ts`
`${REPORTER_PREFIX} siteUrl is not configured in siteMetadata. Please check gatsby-config.ts`,
);
return;
}
Expand All @@ -442,22 +441,16 @@ export const onPostBuild: GatsbyNode['onPostBuild'] = async ({ graphql, reporter
successCount++;
} catch (error) {
const errorMessage = error instanceof Error ? error.message : String(error);
reporter.warn(
`${REPORTER_PREFIX} Failed to transpile ${node.internal.contentFilePath}: ${errorMessage}`
);
reporter.warn(`${REPORTER_PREFIX} Failed to transpile ${node.internal.contentFilePath}: ${errorMessage}`);
failureCount++;
}
}

// Report summary
if (failureCount > 0) {
reporter.warn(
`${REPORTER_PREFIX} Transpiled ${successCount} files, ${failureCount} failed`
);
reporter.warn(`${REPORTER_PREFIX} Transpiled ${successCount} files, ${failureCount} failed`);
} else {
reporter.info(
`${REPORTER_PREFIX} Successfully transpiled ${successCount} MDX files to Markdown`
);
reporter.info(`${REPORTER_PREFIX} Successfully transpiled ${successCount} MDX files to Markdown`);
}
};

Expand Down
4 changes: 0 additions & 4 deletions src/components/Layout/Header.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,6 @@ jest.mock('@ably/ui/core/LinkButton', () => {
return MockButton;
});

jest.mock('../SearchBar', () => ({
SearchBar: jest.fn(() => <div>SearchBar</div>),
}));

jest.mock('./LeftSidebar', () => ({
__esModule: true,
default: jest.fn(() => <div>LeftSidebar</div>),
Expand Down
Loading