@sentry/remix
Version:
Official Sentry SDK for Remix
98 lines (95 loc) • 3.63 kB
JavaScript
import * as path from 'path';
import { createRemixRouteManifest } from '../config/createRemixRouteManifest.js';
function escapeJsonForHtml(jsonString) {
return jsonString.replace(/<\//g, "<\\/").replace(/<!--/g, "<\\!--");
}
const MANIFEST_GLOBAL_KEY = "_sentryRemixRouteManifest";
function makeRouteManifestPlugin(options = {}) {
let routeManifestJson = "";
let isDevMode = false;
return {
name: "sentry-remix-route-manifest",
enforce: "post",
configResolved(config) {
isDevMode = config.command === "serve" || config.mode === "development";
try {
const rootDir = config.root || process.cwd();
let resolvedAppDirPath = options.appDirPath;
if (resolvedAppDirPath && !path.isAbsolute(resolvedAppDirPath)) {
resolvedAppDirPath = path.resolve(rootDir, resolvedAppDirPath);
}
const manifest = createRemixRouteManifest({
appDirPath: resolvedAppDirPath,
rootDir
});
routeManifestJson = JSON.stringify(manifest);
if (isDevMode) {
console.log(
`[Sentry Remix] Found ${manifest.staticRoutes.length} static and ${manifest.dynamicRoutes.length} dynamic routes`
);
}
} catch (error) {
console.error("[Sentry Remix] Failed to generate route manifest:", error);
routeManifestJson = JSON.stringify({ dynamicRoutes: [], staticRoutes: [] });
}
},
transformIndexHtml: {
order: "pre",
handler(html) {
if (!routeManifestJson) {
return html;
}
const safeJsonValue = escapeJsonForHtml(JSON.stringify(routeManifestJson));
const script = `<script>window.${MANIFEST_GLOBAL_KEY} = ${safeJsonValue};<\/script>`;
if (/<head>/i.test(html)) {
return html.replace(/<head>/i, (match) => `${match}
${script}`);
}
if (/<html[^>]*>/i.test(html)) {
return html.replace(/<html[^>]*>/i, (match) => `${match}
<head>${script}</head>`);
}
return `<!DOCTYPE html><html><head>${script}</head><body>${html}</body></html>`;
}
},
transform(code, id) {
if (!routeManifestJson) {
return null;
}
const isClientEntry = /entry[.-]client\.[jt]sx?$/.test(id) || // Also handle Remix's default entry.client location
id.includes("/entry.client.") || id.includes("/entry-client.");
const isServerEntry = /entry[.-]server\.[jt]sx?$/.test(id) || // Also handle Remix's default entry.server location
id.includes("/entry.server.") || id.includes("/entry-server.") || // Also handle Hydrogen/Cloudflare Workers server files
/(^|\/)server\.[jt]sx?$/.test(id);
if (isClientEntry) {
const safeJsonValue = escapeJsonForHtml(JSON.stringify(routeManifestJson));
const injectedCode = `
// Sentry Remix Route Manifest - Auto-injected
if (typeof window !== 'undefined') {
window.${MANIFEST_GLOBAL_KEY} = window.${MANIFEST_GLOBAL_KEY} || ${safeJsonValue};
}
${code}`;
return {
code: injectedCode,
map: null
};
}
if (isServerEntry) {
const safeJsonValue = escapeJsonForHtml(JSON.stringify(routeManifestJson));
const injectedCode = `
// Sentry Remix Route Manifest - Auto-injected
if (typeof globalThis !== 'undefined') {
globalThis.${MANIFEST_GLOBAL_KEY} = globalThis.${MANIFEST_GLOBAL_KEY} || ${safeJsonValue};
}
${code}`;
return {
code: injectedCode,
map: null
};
}
return null;
}
};
}
export { makeRouteManifestPlugin };
//# sourceMappingURL=routeManifestPlugin.js.map