Skip to content

Commit 7824c40

Browse files
chargomeclaude
andauthored
fix(nextjs): Stop Turbopack loaders from loading the Sentry CLI (#24651)
Our Turbopack loaders imported helpers from `@sentry/bundler-plugins/core`, which also loads the `sentry` CLI package. The loaders need only two small helpers, so this adds a separate internal entry point, `@sentry/bundler-plugins/loader-utils`, that exports just those and doesn't touch the CLI. This came up because the Next.js 16 canary build with Turbopack fails: Turbopack follows the loaders' imports into the CLI package and tries to compile one of its TypeScript type files (`index.d.cts`) as code. That's a bug on the Next.js side, but the loaders shouldn't pull in the CLI in the first place. Fixes #24650 Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 95a96a8 commit 7824c40

10 files changed

Lines changed: 72 additions & 46 deletions

File tree

‎packages/bundler-plugins/package.json‎

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,11 @@
4444
"import": "./build/esm/core/index.js",
4545
"require": "./build/cjs/core/index.js"
4646
},
47+
"./loader-utils": {
48+
"types": "./build/types/loader-utils/index.d.ts",
49+
"import": "./build/esm/loader-utils/index.js",
50+
"require": "./build/cjs/loader-utils/index.js"
51+
},
4752
"./babel-plugin": {
4853
"types": "./build/types/babel-plugin/index.d.ts",
4954
"import": "./build/esm/babel-plugin/index.js",

‎packages/bundler-plugins/rollup.npm.config.mjs‎

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ export default makeNPMConfigVariants(
44
makeBaseNPMConfig({
55
entrypoints: [
66
'src/core/index.ts',
7+
'src/loader-utils/index.ts',
78
'src/rollup/index.ts',
89
'src/vite/index.ts',
910
'src/esbuild/index.ts',
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
import { createOxcComponentNameAnnotateHooks, getOxcParseAstAsync } from './component-annotation-oxc';
2+
import type { ComponentAnnotationTransformMeta, ParseAstAsync } from './component-annotation-oxc-ast';
3+
import type { Logger } from './logger';
4+
5+
const PARSER_UNAVAILABLE_MESSAGE =
6+
'Could not load `oxc-parser` for this platform. React components will not be annotated.';
7+
8+
// Module level, because the Turbopack loader creates new hooks for
9+
// every file.
10+
let warnedParserUnavailable = false;
11+
12+
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
13+
export function createComponentNameAnnotateHooks(
14+
ignoredComponents: string[],
15+
injectIntoHtml: boolean,
16+
options: { getParseAstAsync?: () => Promise<ParseAstAsync | null>; logger?: Logger } = {},
17+
) {
18+
const hooks = createOxcComponentNameAnnotateHooks(
19+
ignoredComponents,
20+
async () => {
21+
const parseAstAsync = (await options.getParseAstAsync?.()) ?? (await getOxcParseAstAsync());
22+
23+
if (!parseAstAsync && !warnedParserUnavailable) {
24+
warnedParserUnavailable = true;
25+
if (options.logger) {
26+
options.logger.warn(PARSER_UNAVAILABLE_MESSAGE);
27+
} else {
28+
// eslint-disable-next-line no-console
29+
console.warn(`[@sentry/bundler-plugins] ${PARSER_UNAVAILABLE_MESSAGE}`);
30+
}
31+
}
32+
33+
return parseAstAsync;
34+
},
35+
injectIntoHtml,
36+
);
37+
38+
return {
39+
transform(this: void, code: string, id: string, meta?: ComponentAnnotationTransformMeta) {
40+
return hooks.transform(code, id, meta);
41+
},
42+
};
43+
}

‎packages/bundler-plugins/src/core/index.ts‎

Lines changed: 1 addition & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,4 @@
11
import { CodeInjection, containsOnlyImports, stripQueryAndHashFromPath } from './utils';
2-
import { createOxcComponentNameAnnotateHooks, getOxcParseAstAsync } from './component-annotation-oxc';
3-
import type { ComponentAnnotationTransformMeta, ParseAstAsync } from './component-annotation-oxc-ast';
4-
import type { Logger } from './logger';
52

63
/**
74
* Checks if a file is a JavaScript file based on its extension.
@@ -43,45 +40,7 @@ export function shouldSkipCodeInjection(code: string, facadeModuleId: string | n
4340
export { globFiles } from './glob';
4441
export { getCodeInjectionPosition } from './get-code-injection-position';
4542

46-
const PARSER_UNAVAILABLE_MESSAGE =
47-
'Could not load `oxc-parser` for this platform. React components will not be annotated.';
48-
49-
// Module level, because the Turbopack loader creates new hooks for
50-
// every file.
51-
let warnedParserUnavailable = false;
52-
53-
// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
54-
export function createComponentNameAnnotateHooks(
55-
ignoredComponents: string[],
56-
injectIntoHtml: boolean,
57-
options: { getParseAstAsync?: () => Promise<ParseAstAsync | null>; logger?: Logger } = {},
58-
) {
59-
const hooks = createOxcComponentNameAnnotateHooks(
60-
ignoredComponents,
61-
async () => {
62-
const parseAstAsync = (await options.getParseAstAsync?.()) ?? (await getOxcParseAstAsync());
63-
64-
if (!parseAstAsync && !warnedParserUnavailable) {
65-
warnedParserUnavailable = true;
66-
if (options.logger) {
67-
options.logger.warn(PARSER_UNAVAILABLE_MESSAGE);
68-
} else {
69-
// eslint-disable-next-line no-console
70-
console.warn(`[@sentry/bundler-plugins] ${PARSER_UNAVAILABLE_MESSAGE}`);
71-
}
72-
}
73-
74-
return parseAstAsync;
75-
},
76-
injectIntoHtml,
77-
);
78-
79-
return {
80-
transform(this: void, code: string, id: string, meta?: ComponentAnnotationTransformMeta) {
81-
return hooks.transform(code, id, meta);
82-
},
83-
};
84-
}
43+
export { createComponentNameAnnotateHooks } from './component-annotate-hooks';
8544

8645
export function getDebugIdSnippet(debugId: string): CodeInjection {
8746
return new CodeInjection(
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
// Internal to Sentry SDKs, not public API. Kept separate from `./core` so bundler loaders
2+
// don't pull in the build plugin manager and the `sentry` CLI.
3+
export { getCodeInjectionPosition } from '../core/get-code-injection-position';
4+
export { createComponentNameAnnotateHooks } from '../core/component-annotate-hooks';
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
import { describe, expect, it, vi } from 'vitest';
2+
3+
vi.mock('sentry', () => {
4+
throw new Error('`loader-utils` must not import the `sentry` CLI');
5+
});
6+
7+
describe('loader-utils', () => {
8+
it('exports the loader helpers without loading the sentry CLI', async () => {
9+
const loaderUtils = await import('../../src/loader-utils');
10+
11+
expect(loaderUtils.getCodeInjectionPosition).toBeInstanceOf(Function);
12+
expect(loaderUtils.createComponentNameAnnotateHooks).toBeInstanceOf(Function);
13+
});
14+
});

‎packages/nextjs/src/config/loaders/componentAnnotationLoader.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createComponentNameAnnotateHooks } from '@sentry/bundler-plugins/core';
1+
import { createComponentNameAnnotateHooks } from '@sentry/bundler-plugins/loader-utils';
22
import type { LoaderThis } from './types';
33

44
export type ComponentAnnotationLoaderOptions = {

‎packages/nextjs/src/config/loaders/moduleMetadataInjectionLoader.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getCodeInjectionPosition } from '@sentry/bundler-plugins/core';
1+
import { getCodeInjectionPosition } from '@sentry/bundler-plugins/loader-utils';
22
import type { LoaderThis } from './types';
33

44
export type ModuleMetadataInjectionLoaderOptions = {

‎packages/nextjs/src/config/loaders/valueInjectionLoader.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { getCodeInjectionPosition } from '@sentry/bundler-plugins/core';
1+
import { getCodeInjectionPosition } from '@sentry/bundler-plugins/loader-utils';
22
import type { LoaderThis } from './types';
33

44
export type ValueInjectionLoaderOptions = {

‎packages/nextjs/test/config/loaders/componentAnnotationLoader.test.ts‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ const { mockTransform, mockCreateHooks } = vi.hoisted(() => {
99
return { mockTransform, mockCreateHooks };
1010
});
1111

12-
vi.mock('@sentry/bundler-plugins/core', () => ({
12+
vi.mock('@sentry/bundler-plugins/loader-utils', () => ({
1313
createComponentNameAnnotateHooks: mockCreateHooks,
1414
}));
1515

0 commit comments

Comments
 (0)