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
22 changes: 19 additions & 3 deletions packages/server-utils/src/orchestrion/bundler/rollup.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
import codeTransformer from '@apm-js-collab/code-transformer-bundler-plugins/rollup';
import type { ExternalOption, InputOptions, NormalizedInputOptions, Plugin, PluginContext } from 'rollup';
import type {
ExternalOption,
InputOptions,
NormalizedInputOptions,
Plugin,
PluginContext,
ResolveIdHook,
} from 'rollup';

export type { Plugin as RollupPlugin } from 'rollup';
import { instrumentedModuleNames } from '../config';
Expand Down Expand Up @@ -60,11 +67,20 @@ export function sentryOrchestrionPlugin(options: PluginOptions = {}): Plugin {
// specifier doesn't resolve from an instrumented package's location, so when
// normal resolution fails, fall back to this package's own resolution so it
// gets bundled from its real on-disk path.
async resolveId(this: PluginContext, source: string, importer: string | undefined) {
//
// Forward `options` unchanged: it carries the `custom` metadata `@rollup/plugin-commonjs`
// uses to recognize a `require()` it is already resolving. Drop it and that plugin warns
// (THIS_RESOLVE_WITHOUT_OPTIONS), then abandons the resolution.
async resolveId(
this: PluginContext,
source: string,
importer: string | undefined,
options: Parameters<ResolveIdHook>[2],
) {
if (source !== SNIPPET_IMPORT_SPECIFIER) {
return null;
}
const resolved = await this.resolve(source, importer, { skipSelf: true });
const resolved = await this.resolve(source, importer, { ...options, skipSelf: true });
if (resolved) {
return resolved;
}
Expand Down
40 changes: 40 additions & 0 deletions packages/server-utils/test/orchestrion/bundler.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,46 @@ describe('sentryOrchestrionPlugin (rollup)', () => {
// skips the warning when external is not a normalized predicate
expect(warn).not.toHaveBeenCalled();
});

it('forwards the resolveId options to this.resolve and falls back to self-resolution', async () => {
const plugin = rollupPlugin();
const resolveId = plugin.resolveId as (
this: unknown,
source: string,
importer: string | undefined,
opts: unknown,
) => Promise<unknown>;

const resolve = vi.fn().mockResolvedValue(null);
// `custom` is what `@rollup/plugin-commonjs` uses to recognise its own `require()` resolution;
// dropping it makes the plugin warn (THIS_RESOLVE_WITHOUT_OPTIONS) and abandon the resolution.
const options = { attributes: {}, custom: { 'node-resolve': { isRequire: true } }, isEntry: false };

resolve.mockResolvedValueOnce({ id: '/resolved.js' });
await expect(resolveId.call({ resolve }, '@sentry/server-utils', '/x.js', options)).resolves.toEqual({
id: '/resolved.js',
});
expect(resolve).toHaveBeenCalledWith('@sentry/server-utils', '/x.js', { ...options, skipSelf: true });

// When it fails (pnpm isolation), fall back to this package's own resolution.
const fallback = await resolveId.call({ resolve }, '@sentry/server-utils', '/x.js', options);
expect(typeof fallback).toBe('string');
expect(fallback).toContain('server-utils');
});

it('ignores specifiers other than the injected snippet import', async () => {
const plugin = rollupPlugin();
const resolveId = plugin.resolveId as (
this: unknown,
source: string,
importer: string | undefined,
opts: unknown,
) => Promise<unknown>;

const resolve = vi.fn();
await expect(resolveId.call({ resolve }, 'mysql', '/x.js', { attributes: {}, isEntry: false })).resolves.toBeNull();
expect(resolve).not.toHaveBeenCalled();
});
});

describe('sentryOrchestrionPlugin (esbuild)', () => {
Expand Down
Loading