Last active
May 23, 2023 20:43
-
-
Save benjaminsehl/33efd56fd26faeb70dd3a741578d2df6 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { CacheLong } from '@shopify/hydrogen'; | |
interface Config { | |
cacheControl: string; | |
removeNoIndex: boolean; | |
updateCanonical: boolean; | |
ignoreRedirects: boolean; | |
} | |
const config: Config = { | |
cacheControl: 'public, max-age=3600, stale-while-revalidate=86400', // Set to the amount of time you want to cache the page, in seconds | |
removeNoIndex: true, // Set to false if you want to respect robots noindex tags | |
updateCanonical: true, // Set to false if you want to respect canonical meta tags | |
ignoreRedirects: true, // Set to false if you aren't redirecting to Hydrogen in your theme | |
}; | |
/** | |
* Remove the noindex meta tag from the input data. | |
* @param data - The HTML data to process. | |
* @returns The processed HTML data without the noindex meta tag. | |
*/ | |
function removeNoIndexMetaTag(data: string): string { | |
return data.replace(/<meta.*name="robots".*content="noindex.*".*>/gi, ''); | |
} | |
/** | |
* Update the canonical tag in the input data. | |
* @param data - The HTML data to process. | |
* @param origin - The origin of the request. | |
* @param url - The primary domain URL. | |
* @returns The processed HTML data with the updated canonical tag. | |
*/ | |
function updateCanonicalTag(data: string, origin: string, url: string): string { | |
return data.replace(/<link.*rel="canonical".*href=".*".*>/gi, (match) => { | |
return match.replace(url, origin); | |
}); | |
} | |
/** | |
* Replace the monorailRegion value in the input data. | |
* @param data - The HTML data to process. | |
* @returns The processed HTML data with the updated monorailRegion value. | |
*/ | |
function replaceMonorailRegionValue(data: string): string { | |
return data.replace(/"monorailRegion":"shop_domain"/gi, '"monorailRegion":"global"'); | |
} | |
/** | |
* Remove window.location.replace calls from the input data. | |
* @param data - The HTML data to process. | |
* @returns The processed HTML data without window.location.replace calls. | |
*/ | |
function removeWindowLocationReplaceCalls(data: string): string { | |
return data.replace(/<script\b[^<]*(?:(?!<\/script>)<[^<]*)*<\/script>/gi, (match) => { | |
return match.replace(/window\.location\.replace\([^)]*\);?/g, ''); | |
}); | |
} | |
/** | |
* Process the HTML data by updating meta tags, canonical tags, monorailRegion, and removing window.location.replace calls. | |
* @param data - The HTML data to process. | |
* @param origin - The origin of the request. | |
* @param url - The primary domain URL. | |
* @param config - The configuration object. | |
* @returns The processed HTML data. | |
*/ | |
function processHtmlData(data: string, origin: string, url: string, config: Config): string { | |
let processedData = data; | |
if (config.removeNoIndex) { | |
processedData = removeNoIndexMetaTag(processedData); | |
} | |
if (config.updateCanonical) { | |
processedData = updateCanonicalTag(processedData, origin, url); | |
} | |
processedData = replaceMonorailRegionValue(processedData); | |
if (config.ignoreRedirects) { | |
processedData = removeWindowLocationReplaceCalls(processedData); | |
} | |
processedData = processedData.replace(new RegExp(url, 'g'), origin); | |
return processedData; | |
} | |
export async function loader({ request, context }: { request: Request; context: any }) { | |
try { | |
const { | |
shop: { | |
primaryDomain: { url }, | |
}, | |
} = await context.storefront.query( | |
`#graphql | |
query { | |
shop { | |
primaryDomain { | |
url | |
} | |
} | |
} | |
`, | |
{ | |
cacheControl: config.cacheControl, | |
}, | |
); | |
const { origin, pathname, search } = new URL(request.url); | |
const customHeaders = new Headers({ | |
'X-Shopify-Client-IP': request.headers.get('X-Shopify-Client-IP') || '', | |
'X-Shopify-Client-IP-Sig': request.headers.get('X-Shopify-Client-IP-Sig') || '', | |
'User-Agent': 'Hydrogen', | |
}); | |
const response = await fetch(url + pathname + search, { | |
headers: customHeaders, | |
}); | |
if (response.status === 301) { | |
return redirect(response.headers.get('Location') || ''); | |
} | |
const data = await response.text(); | |
const processedData = processHtmlData(data, origin, url, config); | |
const status = /<title>(.|\n)*404 Not Found(.|\n)*<\/title>/i.test(data) ? 404 : response.status; | |
const headers = new Headers(response.headers); | |
headers.set('content-type', 'text/html'); | |
headers.delete('content-encoding'); | |
headers.set('Cache-Control', config.cacheControl); | |
return new Response(processedData, { status, headers }); | |
} catch (error) { | |
console.error('Error in loader function:', error); | |
return new Response('An error occurred while processing the request.', { status: 500 }); | |
} | |
} |
L122
if (response.status === 301) {
It would be good to add a response.ok
check before L122. If it's not ok, maybe redirect to the homepage or throw an error
L128
const status = /<title>(.|\n)*404 Not Found(.|\n)*<\/title>/i.test(data) ? 404 : response.status;
Where does this <title>404 Not Found</title>
response come from? Hydrogen? If yes, I'm a bit worried that we are relying on this text to be present. If this is a hydrogen error page response, I would consider returning a header
from the 404 like Hydrogen-Error-Page
and reading the header to validate if its indeed a 404.
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Tests: