Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Appearance settings
Draft
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: 5 additions & 0 deletions 5 .changeset/tricky-windows-destroy.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@faustwp/core': patch
---

Fix incorrect sitemap URLs when using sitemapIndexPath
19 changes: 9 additions & 10 deletions 19 packages/faustwp-core/src/server/sitemaps/createSitemaps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,7 @@ export async function createRootSitemapIndex(
throw new Error('Request object must have URL');
}

// get sitemapIndexPath config param
// fetch sitemap from WP
// Get the trimmed sitemap index path
const trimmedWpUrl = trim(getWpUrl(), '/');
const trimmedFrontendUrl = trim(frontendUrl, '/');
const trimmedSitemapIndexPath = trim(
Expand All @@ -75,10 +74,10 @@ export async function createRootSitemapIndex(
let sitemaps: SitemapSchemaSitemapElement[] = [];

if (!isUndefined(pages) && isArray(pages) && pages.length) {
const trimmedFaustPagesPart = `${trim(
SITEMAP_INDEX_PATH,
const trimmedFaustPagesPart = `${trimmedSitemapIndexPath}?sitemap=${trim(
FAUST_PAGES_PATHNAME,
'/',
)}?sitemap=${trim(FAUST_PAGES_PATHNAME, '/')}`;
)}`;
const sitemapFaustPagesUrl = `${trimmedFrontendUrl}/${trimmedFaustPagesPart}`;

sitemaps = [
Expand Down Expand Up @@ -170,11 +169,11 @@ export async function createRootSitemapIndex(
*
* @example
* Replaces http://headless.local/wp-sitemap-posts-page-1.xml with
* http://localhost:3000/wp-sitemap-posts-page-1.xml
* http://localhost:3000/sitemap_index.xml?sitemap=wp-sitemap-posts-page-1.xml
*/
wpSitemaps.forEach((sitemap) => {
const url = new URL(sitemap.loc);
const sitemapUrl = `${trim(frontendUrl, '/')}/sitemap.xml?sitemap=${trim(
const sitemapUrl = `${trimmedFrontendUrl}/${trimmedSitemapIndexPath}?sitemap=${trim(
url.pathname,
'/',
)}`;
Expand Down Expand Up @@ -293,11 +292,11 @@ export async function handleSitemapPath(
let urls: SitemapSchemaUrlElement[] = [];

/**
* Replace the existing WordPress URL in each "loc" with the headless URL
* Replace the existing WordPress URL in each "loc" with the frontend URL
*
* @example
* Replaces http://headless.local/wp-sitemap-posts-page-1.xml with
* http://localhost:3000/wp-sitemap-posts-page-1.xml
* Replaces http://headless.local/page-1/ with
* http://localhost:3000/page-1/
*/
wpSitemapUrls.forEach((url) => {
urls = [
Expand Down
185 changes: 146 additions & 39 deletions 185 packages/faustwp-core/tests/server/sitemaps/createSitemaps.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,18 +15,17 @@ describe('createRootSitemapIndex', () => {
process.env = envBackup;
});

const invalidSitemapIndexXML = `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//headless.local/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl"?>
const invalidSitemapIndexXML = `<?xml version="1.0" encoding="UTF-8"?>
<!-- XML Sitemap generated by Yoast SEO -->`;

const validSitemapIndex1RecordXML = `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//headless.local/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl"?>
const validSitemapIndex1RecordXML = `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>http://headless.local/post-sitemap.xml</loc>
</sitemap>
</sitemapindex>
<!-- XML Sitemap generated by Yoast SEO -->`;
</sitemapindex>`;

const validSitemapIndexXML = `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//headless.local/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl"?>
const validSitemapIndexXML = `<?xml version="1.0" encoding="UTF-8"?>
<sitemapindex xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<sitemap>
<loc>http://headless.local/post-sitemap.xml</loc>
Expand All @@ -41,8 +40,7 @@ describe('createRootSitemapIndex', () => {
<loc>http://headless.local/author-sitemap.xml</loc>
<lastmod>2021-12-17T16:56:55+00:00</lastmod>
</sitemap>
</sitemapindex>
<!-- XML Sitemap generated by Yoast SEO -->`;
</sitemapindex>`;

it('returns undefined when the response to the WP sitemap is not ok', async () => {
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
Expand Down Expand Up @@ -86,10 +84,7 @@ describe('createRootSitemapIndex', () => {
});

it('ignores sitemaps that match the sitemapPathsToIgnore property', async () => {
const createSitemapIndexSpy = jest.spyOn(
sitemapUtils,
'createSitemapIndex',
);
const createSitemapIndexSpy = jest.spyOn(sitemapUtils, 'createSitemapIndex');
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
ok: true,
Expand Down Expand Up @@ -123,10 +118,7 @@ describe('createRootSitemapIndex', () => {
});

it('ignores sitemaps that match a sitemapPathsToIgnore wildcard', async () => {
const createSitemapIndexSpy = jest.spyOn(
sitemapUtils,
'createSitemapIndex',
);
const createSitemapIndexSpy = jest.spyOn(sitemapUtils, 'createSitemapIndex');
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
ok: true,
Expand Down Expand Up @@ -160,10 +152,7 @@ describe('createRootSitemapIndex', () => {
});

it('returns the proper sitemap urls with replacing URLs', async () => {
const createSitemapIndexSpy = jest.spyOn(
sitemapUtils,
'createSitemapIndex',
);
const createSitemapIndexSpy = jest.spyOn(sitemapUtils, 'createSitemapIndex');
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
ok: true,
Expand Down Expand Up @@ -211,10 +200,7 @@ describe('createRootSitemapIndex', () => {
* configured properly. Ensure it returns an array of URLs
*/
it('returns the proper sitemap urls with only 1 record', async () => {
const createSitemapIndexSpy = jest.spyOn(
sitemapUtils,
'createSitemapIndex',
);
const createSitemapIndexSpy = jest.spyOn(sitemapUtils, 'createSitemapIndex');
jest.spyOn(global, 'fetch').mockImplementationOnce(() => {
return Promise.resolve({
ok: true,
Expand Down Expand Up @@ -242,6 +228,130 @@ describe('createRootSitemapIndex', () => {

expect(createSitemapIndexSpy).toHaveBeenCalledWith(expectedSitemaps);
});

it('uses the default sitemapIndexPath when none is provided', async () => {
const createSitemapIndexSpy = jest.spyOn(sitemapUtils, 'createSitemapIndex');
jest.spyOn(global, 'fetch').mockImplementationOnce((url) => {
expect(url).toBe('http://headless.local/sitemap.xml');
return Promise.resolve({
ok: true,
status: 200,
text: () => Promise.resolve(validSitemapIndexXML),
}) as Promise<Response>;
});

const req = {
url: 'http://localhost:3000/sitemap.xml',
} as NextRequest;

const config: GetSitemapPropsConfig = {
frontendUrl: 'http://localhost:3000',
};

await createSitemaps.createRootSitemapIndex(req, config);

const expectedSitemaps = [
{
loc: 'http://localhost:3000/sitemap.xml?sitemap=post-sitemap.xml',
},
{
loc: 'http://localhost:3000/sitemap.xml?sitemap=page-sitemap.xml',
},
{
loc: 'http://localhost:3000/sitemap.xml?sitemap=category-sitemap.xml',
},
{
loc: 'http://localhost:3000/sitemap.xml?sitemap=author-sitemap.xml',
lastmod: '2021-12-17T16:56:55+00:00',
},
];

expect(createSitemapIndexSpy).toHaveBeenCalledWith(expectedSitemaps);
});

it('uses the custom sitemapIndexPath when provided', async () => {
jest.spyOn(global, 'fetch').mockImplementationOnce((url) => {
expect(url).toBe('http://headless.local/custom-sitemap-index.xml');
return Promise.resolve({
ok: true,
status: 200,
text: () => Promise.resolve(validSitemapIndexXML),
}) as Promise<Response>;
});

const req = {
url: 'http://localhost:3000/custom-sitemap-index.xml',
} as NextRequest;

const config: GetSitemapPropsConfig = {
frontendUrl: 'http://localhost:3000',
sitemapIndexPath: '/custom-sitemap-index.xml',
};

const createSitemapIndexSpy = jest.spyOn(sitemapUtils, 'createSitemapIndex');

await createSitemaps.createRootSitemapIndex(req, config);

const expectedSitemaps = [
{
loc: 'http://localhost:3000/custom-sitemap-index.xml?sitemap=post-sitemap.xml',
},
{
loc: 'http://localhost:3000/custom-sitemap-index.xml?sitemap=page-sitemap.xml',
},
{
loc: 'http://localhost:3000/custom-sitemap-index.xml?sitemap=category-sitemap.xml',
},
{
loc: 'http://localhost:3000/custom-sitemap-index.xml?sitemap=author-sitemap.xml',
lastmod: '2021-12-17T16:56:55+00:00',
},
];

expect(createSitemapIndexSpy).toHaveBeenCalledWith(expectedSitemaps);
});

it('constructs correct child sitemap URLs with custom sitemapIndexPath', async () => {
jest.spyOn(global, 'fetch').mockImplementationOnce((url) => {
expect(url).toBe('http://headless.local/sitemap_index.xml');
return Promise.resolve({
ok: true,
status: 200,
text: () => Promise.resolve(validSitemapIndexXML),
}) as Promise<Response>;
});

const req = {
url: 'http://localhost:3000/sitemap_index.xml',
} as NextRequest;

const config: GetSitemapPropsConfig = {
frontendUrl: 'http://localhost:3000',
sitemapIndexPath: '/sitemap_index.xml',
};

const createSitemapIndexSpy = jest.spyOn(sitemapUtils, 'createSitemapIndex');

await createSitemaps.createRootSitemapIndex(req, config);

const expectedSitemaps = [
{
loc: 'http://localhost:3000/sitemap_index.xml?sitemap=post-sitemap.xml',
},
{
loc: 'http://localhost:3000/sitemap_index.xml?sitemap=page-sitemap.xml',
},
{
loc: 'http://localhost:3000/sitemap_index.xml?sitemap=category-sitemap.xml',
},
{
loc: 'http://localhost:3000/sitemap_index.xml?sitemap=author-sitemap.xml',
lastmod: '2021-12-17T16:56:55+00:00',
},
];

expect(createSitemapIndexSpy).toHaveBeenCalledWith(expectedSitemaps);
});
});

describe('createPagesSitemap()', () => {
Expand All @@ -259,7 +369,7 @@ describe('createPagesSitemap()', () => {
frontendUrl: 'http://localhost:3000',
};

let req = {
const req = {
url: 'http://localhost:3000/sitemap-faust-pages.xml',
} as NextRequest;

Expand Down Expand Up @@ -310,7 +420,7 @@ describe('createPagesSitemap()', () => {

const createSitemapSpy = jest.spyOn(sitemapUtils, 'createSitemap');

let req = {
const req = {
url: 'http://localhost:3000/sitemap-faust-pages.xml',
} as NextRequest;

Expand All @@ -330,8 +440,8 @@ describe('handleSitemapPath()', () => {
afterAll(() => {
process.env = envBackup;
});
const validSitemapXML = `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//headless.local/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl"?>
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
const validSitemapXML = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://headless.local/</loc>
</url>
Expand All @@ -347,19 +457,16 @@ describe('handleSitemapPath()', () => {
<changefreq>weekly</changefreq>
<priority>0.5</priority>
</url>
</urlset>`;

</urlset>
<!-- XML Sitemap generated by Yoast SEO -->`;

const validSitemap1RecordXML = `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//headless.local/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl"?>
<urlset xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:image="http://www.google.com/schemas/sitemap-image/1.1" xsi:schemaLocation="http://www.sitemaps.org/schemas/sitemap/0.9 http://www.sitemaps.org/schemas/sitemap/0.9/sitemap.xsd http://www.google.com/schemas/sitemap-image/1.1 http://www.google.com/schemas/sitemap-image/1.1/sitemap-image.xsd" xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
const validSitemap1RecordXML = `<?xml version="1.0" encoding="UTF-8"?>
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
<url>
<loc>http://headless.local/about</loc>
</url>
</urlset>
<!-- XML Sitemap generated by Yoast SEO -->`;
</urlset>`;

const invalidSitemapXML = `<?xml version="1.0" encoding="UTF-8"?><?xml-stylesheet type="text/xsl" href="//headless.local/wp-content/plugins/wordpress-seo/css/main-sitemap.xsl"?>
const invalidSitemapXML = `<?xml version="1.0" encoding="UTF-8"?>
<!-- XML Sitemap generated by Yoast SEO -->`;

it('returns undefined when the response to the WP sitemap is not ok', async () => {
Expand All @@ -371,7 +478,7 @@ describe('handleSitemapPath()', () => {
});

const req = {
url: 'http://localhost:3000/sitemap-posts.xml',
url: 'http://localhost:3000/sitemap.xml?sitemap=post-sitemap.xml',
} as NextRequest;

const config: GetSitemapPropsConfig = {
Expand All @@ -392,7 +499,7 @@ describe('handleSitemapPath()', () => {
});

const req = {
url: 'http://localhost:3000/sitemap-posts.xml',
url: 'http://localhost:3000/sitemap.xml?sitemap=post-sitemap.xml',
} as NextRequest;

const config: GetSitemapPropsConfig = {
Expand All @@ -415,7 +522,7 @@ describe('handleSitemapPath()', () => {
});

const req = {
url: 'http://localhost:5000/sitemap-pages.xml',
url: 'http://localhost:5000/sitemap.xml?sitemap=page-sitemap.xml',
} as NextRequest;

const config: GetSitemapPropsConfig = {
Expand Down Expand Up @@ -461,7 +568,7 @@ describe('handleSitemapPath()', () => {
});

const req = {
url: 'http://localhost:3000/sitemap-pages.xml',
url: 'http://localhost:3000/sitemap.xml?sitemap=page-sitemap.xml',
} as NextRequest;

const config: GetSitemapPropsConfig = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -105,4 +105,35 @@ describe('validateConfig', () => {
'The pages path property must be a string',
);
});

it('throws an error if sitemapIndexPath is not a string', () => {
const config: any = {
frontendUrl: 'http://localhost:3000',
sitemapIndexPath: 123,
};

expect(() => getSitemapProps.validateConfig(config)).toThrow(
'sitemapIndexPath must be a string',
);
});

it('throws an error if sitemapIndexPath does not start with a forward slash', () => {
const config: any = {
frontendUrl: 'http://localhost:3000',
sitemapIndexPath: 'sitemap_index.xml',
};

expect(() => getSitemapProps.validateConfig(config)).toThrow(
'sitemapIndexPath must start with a forward slash',
);
});

it('passes validation when sitemapIndexPath is a valid string', () => {
const config: any = {
frontendUrl: 'http://localhost:3000',
sitemapIndexPath: '/sitemap_index.xml',
};

expect(() => getSitemapProps.validateConfig(config)).not.toThrow();
});
});
Loading
Morty Proxy This is a proxified and sanitized view of the page, visit original site.