Skip to content

Commit 77fff54

Browse files
author
政宇 廖
committed
fix: Links in blog posts rendered in a feed (rss/atom/json) should be absolute
1 parent ff22728 commit 77fff54

File tree

4 files changed

+59
-1
lines changed

4 files changed

+59
-1
lines changed

packages/docusaurus-plugin-content-blog/src/feed.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,11 @@ import path from 'path';
99
import fs from 'fs-extra';
1010
import logger from '@docusaurus/logger';
1111
import {Feed, type Author as FeedAuthor} from 'feed';
12-
import {normalizeUrl, readOutputHTMLFile} from '@docusaurus/utils';
12+
import {
13+
isValidRelativePath,
14+
normalizeUrl,
15+
readOutputHTMLFile,
16+
} from '@docusaurus/utils';
1317
import {blogPostContainerID} from '@docusaurus/utils-common';
1418
import {load as cheerioLoad} from 'cheerio';
1519
import type {DocusaurusConfig} from '@docusaurus/types';
@@ -105,6 +109,20 @@ async function defaultCreateFeedItems({
105109
);
106110
const $ = cheerioLoad(content);
107111

112+
// Prefix the relative paths with url defined in config
113+
// leave the anchors and external links untouched
114+
// (e.g. href that starts with "#", "http://" or "https://")
115+
$(`div#${blogPostContainerID} a`).each((_, elm) => {
116+
const {href} = elm.attribs;
117+
if (href) {
118+
if (isValidRelativePath(href)) {
119+
// a valid relative url path might not have leading slash
120+
elm.attribs.href =
121+
siteConfig.url + href.startsWith('/') ? href : `/${href}`;
122+
}
123+
}
124+
});
125+
108126
const link = normalizeUrl([siteUrl, permalink]);
109127
const feedItem: BlogFeedItem = {
110128
title: metadataTitle,

packages/docusaurus-utils/src/__tests__/urlUtils.test.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ import {
1818
buildSshUrl,
1919
buildHttpsUrl,
2020
hasSSHProtocol,
21+
isValidRelativePath,
2122
} from '../urlUtils';
2223

2324
describe('normalizeUrl', () => {
@@ -312,3 +313,18 @@ describe('hasSSHProtocol', () => {
312313
expect(hasSSHProtocol(url)).toBe(false);
313314
});
314315
});
316+
317+
describe('isValidRelativePath', () => {
318+
it('works', () => {
319+
expect(isValidRelativePath('https://docusaurus.io/blog')).toBe(false);
320+
expect(isValidRelativePath('notValidhttps://')).toBe(false);
321+
expect(isValidRelativePath('#hash')).toBe(false);
322+
expect(isValidRelativePath('/#hash')).toBe(true);
323+
expect(isValidRelativePath('/blog')).toBe(true);
324+
expect(isValidRelativePath('blog/my-first-blog')).toBe(true);
325+
expect(isValidRelativePath('/blog///valid//')).toBe(true);
326+
expect(isValidRelativePath('/blog/hi#你好')).toBe(true);
327+
expect(isValidRelativePath('')).toBe(true);
328+
expect(isValidRelativePath('/blog?qs=ho')).toBe(true);
329+
});
330+
});

packages/docusaurus-utils/src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ export {
5454
hasSSHProtocol,
5555
buildHttpsUrl,
5656
buildSshUrl,
57+
isValidRelativePath,
5758
} from './urlUtils';
5859
export {
5960
type Tag,

packages/docusaurus-utils/src/urlUtils.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -232,3 +232,26 @@ export function hasSSHProtocol(sourceRepoUrl: string): boolean {
232232
return /^(?:[\w-]+@)?[\w.-]+:[\w./-]+/.test(sourceRepoUrl);
233233
}
234234
}
235+
236+
/**
237+
* Whether the str is a valid relative url path
238+
*/
239+
export function isValidRelativePath(str: string): boolean {
240+
// When str starts with "#", the parsedPath will be "/#xxx"
241+
// and the functions will return true while this is not a relative path
242+
// so an early return is required here
243+
if (str.startsWith('#')) {
244+
return false;
245+
}
246+
try {
247+
const urlObj = new URL(str, 'https://domain.com');
248+
const parsedPath = `${urlObj.pathname}${urlObj.hash}`;
249+
const strToCompare = (str.startsWith('/') ? str : `/${str}`).split('?')[0];
250+
return (
251+
parsedPath === strToCompare ||
252+
parsedPath === encodeURI(strToCompare ?? '')
253+
);
254+
} catch {
255+
return false;
256+
}
257+
}

0 commit comments

Comments
 (0)