Skip to content
Open
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
41 changes: 39 additions & 2 deletions packages/astro/src/integration/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,7 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
}

const isCloudflare = config?.adapter?.name?.startsWith('@astrojs/cloudflare');
const isCloudflareWorkers = isCloudflare && !isCloudflarePages();

if (isCloudflare) {
try {
Expand Down Expand Up @@ -191,8 +192,8 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
injectScript('page-ssr', buildServerSnippet(options || {}));
}

if (isCloudflare && command !== 'dev') {
// For Cloudflare production builds, additionally use a Vite plugin to:
if (isCloudflareWorkers && command !== 'dev') {
// For Cloudflare Workers production builds, additionally use a Vite plugin to:
// 1. Import the server config at the Worker entry level (so Sentry.init() runs
// for ALL requests, not just SSR pages — covers actions and API routes)
// 2. Wrap the default export with `withSentry` from @sentry/cloudflare for
Expand All @@ -215,6 +216,7 @@ export const sentryAstro = (options: SentryOptions = {}): AstroIntegration => {
// Ref: https://developers.cloudflare.com/workers/runtime-apis/nodejs/
updateConfig({
vite: {
plugins: [sentryCloudflareNodeWarningPlugin()],
ssr: {
// @sentry/node is required in case we have 2 different @sentry/node
// packages installed in the same project.
Expand Down Expand Up @@ -255,6 +257,41 @@ function findDefaultSdkInitFile(type: 'server' | 'client'): string | undefined {
.find(filename => fs.existsSync(filename));
}

/**
* Detects if the project is a Cloudflare Pages project by checking for
* `pages_build_output_dir` in the wrangler configuration file.
*
* Cloudflare Pages projects use `pages_build_output_dir` while Workers projects
* use `assets.directory` or `main` fields instead.
*/
function isCloudflarePages(): boolean {
const cwd = process.cwd();
const configFiles = ['wrangler.jsonc', 'wrangler.json', 'wrangler.toml'];

for (const configFile of configFiles) {
const configPath = path.join(cwd, configFile);

if (!fs.existsSync(configPath)) {
continue;
}

const content = fs.readFileSync(configPath, 'utf-8');

if (configFile.endsWith('.toml')) {
// https://regex101.com/r/Uxe4p0/1
// Match pages_build_output_dir as a TOML key (at start of line, ignoring whitespace)
// This avoids false positives from comments (lines starting with #)
return /^\s*pages_build_output_dir\s*=/m.test(content);
}

// Match "pages_build_output_dir" as a JSON key (followed by :)
// This works for both .json and .jsonc without needing to strip comments
return /"pages_build_output_dir"\s*:/.test(content);
Copy link

Choose a reason for hiding this comment

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

JSONC regex matches pages_build_output_dir inside comments

Low Severity

The TOML regex uses ^\s* anchoring with the m flag to avoid matching pages_build_output_dir inside # comments, but the JSON/JSONC regex /"pages_build_output_dir"\s*:/ has no such protection. In a .jsonc file, a commented-out key like // "pages_build_output_dir": "./dist" would still match, causing a Workers project to be misidentified as Pages. This would skip the withSentry wrapper, losing async context propagation and per-request isolation. Anchoring the JSON regex to start-of-line (/^\s*"pages_build_output_dir"\s*:/m) would provide equivalent comment safety as the TOML path.

Additional Locations (1)

Fix in Cursor Fix in Web

}

return false;
}

function getSourcemapsAssetsGlob(config: AstroConfig): string {
// The vercel adapter puts the output into its .vercel directory
// However, the way this adapter is written, the config.outDir value is update too late for
Expand Down
Loading
Loading