Skip to content
Merged
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
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
import type { SentryRollupPluginOptions } from "@sentry/bundler-plugins/rollup";
export declare const sentryConfig: SentryRollupPluginOptions;
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
export const sentryConfig = {
telemetry: false,
sourcemaps: {
disable: "disable-upload",
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { sentryVitePlugin } from "@sentry/bundler-plugins/vite";
import { defineConfig } from "vite";
import { sentryConfig } from "../configs/sourcemaps-disable-upload.config.js";

export default defineConfig({
build: {
minify: false,
sourcemap: true,
rollupOptions: {
input: "src/dynamic-import.js",
output: {
dir: "out/sourcemaps-disable-upload",
entryFileNames: "[name].js",
chunkFileNames: "[name].js",
},
},
},
plugins: [sentryVitePlugin(sentryConfig)],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { expect } from "vitest";
import { test } from "./utils";

// Vite rewrites the source map of chunks with dynamic imports after the Sentry plugin stamps it.
// The debug ID must survive that for every chunk. Files are read raw here because
// `readOutputFiles` zeroes debug IDs, which would hide a mismatch between chunk and map.
test(import.meta.url, ({ runBundler, outDir }) => {
runBundler();

const entry = readFileSync(join(outDir, "dynamic-import.js"), "utf-8");
expect(entry).toContain("__vitePreload");

for (const fileName of ["dynamic-import.js", "import.js"]) {
const chunk = readFileSync(join(outDir, fileName), "utf-8");
const debugId = chunk.match(/sentry-dbid-([0-9a-f-]{36})/)?.[1];
const map = JSON.parse(readFileSync(join(outDir, `${fileName}.map`), "utf-8"));

expect(debugId).toBeDefined();
expect(chunk).toContain(`//# debugId=${debugId}`);
expect(map.debug_id).toBe(debugId);
expect(map.debugId).toBe(debugId);
Comment thread
cursor[bot] marked this conversation as resolved.
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function load() {
return import("./import.js");
}

load();
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { sentryVitePlugin } from "@sentry/bundler-plugins/vite";
import { defineConfig } from "vite";
import { sentryConfig } from "../configs/sourcemaps-disable-upload.config.js";

export default defineConfig({
build: {
minify: false,
sourcemap: true,
rollupOptions: {
input: "src/dynamic-import.js",
output: {
dir: "out/sourcemaps-disable-upload",
entryFileNames: "[name].js",
chunkFileNames: "[name].js",
},
},
},
plugins: [sentryVitePlugin(sentryConfig)],
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import { readFileSync } from "node:fs";
import { join } from "node:path";
import { expect } from "vitest";
import { test } from "./utils";

// Vite rewrites the source map of chunks with dynamic imports after the Sentry plugin stamps it.
// The debug ID must survive that for every chunk. Files are read raw here because
// `readOutputFiles` zeroes debug IDs, which would hide a mismatch between chunk and map.
test(import.meta.url, ({ runBundler, outDir }) => {
runBundler();

const entry = readFileSync(join(outDir, "dynamic-import.js"), "utf-8");
expect(entry).toContain("__vitePreload");

for (const fileName of ["dynamic-import.js", "import.js"]) {
const chunk = readFileSync(join(outDir, fileName), "utf-8");
const debugId = chunk.match(/sentry-dbid-([0-9a-f-]{36})/)?.[1];
const map = JSON.parse(readFileSync(join(outDir, `${fileName}.map`), "utf-8"));

expect(debugId).toBeDefined();
expect(chunk).toContain(`//# debugId=${debugId}`);
expect(map.debug_id).toBe(debugId);
expect(map.debugId).toBe(debugId);
}
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export async function load() {
return import("./import.js");
}

load();
32 changes: 32 additions & 0 deletions packages/bundler-plugins/src/rollup/debug-id-stamping.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
import { isJsFile, stampDebugId } from '../core';

// The subset of Rollup's `OutputBundle` the stamping hooks read.
export type OutputBundle = Record<
string,
| { type: 'chunk'; fileName: string; code: string; sourcemapFileName?: string | null }
| { type: 'asset'; fileName: string; source: string | Uint8Array }
>;

export function stampDebugIds(bundle: OutputBundle, updateChunkCode: boolean): void {
for (const output of Object.values(bundle)) {
if (output.type !== 'chunk' || !isJsFile(output.fileName)) {
continue;
}

const sourceMapAsset = bundle[output.sourcemapFileName ?? `${output.fileName}.map`];
const sourceMapSource =
sourceMapAsset?.type === 'asset' && typeof sourceMapAsset.source === 'string' ? sourceMapAsset.source : undefined;

const stamped = stampDebugId(output.code, sourceMapSource);
if (!stamped) {
continue;
}

if (updateChunkCode) {
output.code = stamped.bundleSource;
}
if (stamped.sourceMapSource !== undefined && sourceMapAsset?.type === 'asset') {
sourceMapAsset.source = stamped.sourceMapSource;
}
}
}
31 changes: 2 additions & 29 deletions packages/bundler-plugins/src/rollup/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@ import {
createComponentNameAnnotateHooks,
replaceBooleanFlagsInCode,
CodeInjection,
stampDebugId,
getCodeInjectionPosition,
} from '../core';
import type { ComponentAnnotationTransformMeta } from '../core/component-annotation-oxc';
Expand All @@ -20,19 +19,13 @@ import MagicString from 'magic-string';
import * as path from 'node:path';
import { createRequire } from 'node:module';
import { finalizeRolldownDebugIds, getDebugIdForChunk, hasExistingDebugID } from './debug-id-injection';
import { stampDebugIds, type OutputBundle } from './debug-id-stamping';

// The subset of Rollup's `TransformResult` that this plugin's `transform`
// hook actually returns. Defined locally instead of imported from `rollup`
// because `rollup` is an optional dependency.
type TransformResult = { code: string; map?: SourceMap | string | { mappings: string } | null } | null | undefined;

// The subset of Rollup's `OutputBundle` the stamping hook reads.
type OutputBundle = Record<
string,
| { type: 'chunk'; fileName: string; code: string; sourcemapFileName?: string | null }
| { type: 'asset'; fileName: string; source: string | Uint8Array }
>;

type ViteModule = {
parseAstAsync?: (code: string, options: { lang: 'jsx' | 'tsx' }) => Promise<unknown>;
};
Expand Down Expand Up @@ -251,27 +244,7 @@ export function _rollupPluginInternal(
return;
}

for (const output of Object.values(bundle)) {
if (output.type !== 'chunk' || !isJsFile(output.fileName)) {
continue;
}

const sourceMapAsset = bundle[output.sourcemapFileName ?? `${output.fileName}.map`];
const sourceMapSource =
sourceMapAsset?.type === 'asset' && typeof sourceMapAsset.source === 'string'
? sourceMapAsset.source
: undefined;

const stamped = stampDebugId(output.code, sourceMapSource);
if (!stamped) {
continue;
}

output.code = stamped.bundleSource;
if (stamped.sourceMapSource !== undefined && sourceMapAsset?.type === 'asset') {
sourceMapAsset.source = stamped.sourceMapSource;
}
}
stampDebugIds(bundle, true);
}

async function writeBundle(
Expand Down
27 changes: 25 additions & 2 deletions packages/bundler-plugins/src/vite/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,15 @@
import type { SentryRollupPluginOptions } from '../rollup';
import { _rollupPluginInternal } from '../rollup';
import { createRequire } from 'node:module';
import { stampDebugIds, type OutputBundle } from '../rollup/debug-id-stamping';

interface SentryVitePlugin {
name: string;
enforce: 'pre';
enforce: 'pre' | 'post';
generateBundle?: {
order: 'pre' | 'post';
handler: (_outputOptions: unknown, bundle: OutputBundle) => void;
};
}

function getViteMajorVersion(): string | undefined {
Expand All @@ -22,12 +27,30 @@ function getViteMajorVersion(): string | undefined {
}

export const sentryVitePlugin = (options?: SentryRollupPluginOptions): SentryVitePlugin[] => {
return [
const plugins: SentryVitePlugin[] = [
{
enforce: 'pre',
..._rollupPluginInternal(options, 'vite', getViteMajorVersion()),
},
];

if (!options?.disable && options?.sourcemaps?.disable === 'disable-upload') {
plugins.push({
name: 'sentry-vite-debug-id-sourcemaps',
enforce: 'post',
generateBundle: {
order: 'post',
handler(_outputOptions, bundle) {
// Vite's module preload rewrite in `generateBundle` regenerates the source map of every
// chunk with a dynamic import, which drops the debug ID the main plugin stamped in its
// `pre` hook. Restore it without touching the chunk code, so file hashes stay valid.
stampDebugIds(bundle, false);
},
},
});
}

return plugins;
};

export type { Options as SentryVitePluginOptions } from '../core';
Loading