Skip to content

Commit 82fa9fb

Browse files
committed
implement supportsNativeDebugIds
1 parent 0dd6ed2 commit 82fa9fb

File tree

2 files changed

+147
-4
lines changed

2 files changed

+147
-4
lines changed

packages/nextjs/src/config/util.ts

Lines changed: 31 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -68,19 +68,47 @@ export function supportsProductionCompileHook(version: string): boolean {
6868

6969
/**
7070
* Checks if the current Next.js version supports native debug ids for turbopack.
71-
* This feature was first introduced in Next.js v15.6.0-canary.36
71+
* This feature was first introduced in Next.js v15.6.0-canary.36 and marked stable in Next.js v16
7272
*
7373
* @param version - version string to check.
7474
* @returns true if Next.js version supports native debug ids for turbopack builds
7575
*/
7676
export function supportsNativeDebugIds(version: string): boolean {
77-
// tbd
78-
if (version === '15.6.0-canary.36') {
77+
if (!version) {
78+
return false;
79+
}
80+
81+
const { major, minor, prerelease } = parseSemver(version);
82+
83+
if (major === undefined || minor === undefined) {
84+
return false;
85+
}
86+
87+
// Next.js 16+ supports native debug ids
88+
if (major >= 16) {
7989
return true;
8090
}
91+
92+
// For Next.js 15, check if it's 15.6.0-canary.36+
93+
if (major === 15 && prerelease?.startsWith('canary.')) {
94+
// Any canary version 15.7+ supports native debug ids
95+
if (minor > 6) {
96+
return true;
97+
}
98+
99+
// For 15.6 canary versions, check if it's canary.36 or higher
100+
if (minor === 6) {
101+
const canaryNumber = parseInt(prerelease.split('.')[1] || '0', 10);
102+
if (canaryNumber >= 36) {
103+
return true;
104+
}
105+
}
106+
}
107+
81108
return false;
82109
}
83110

111+
/**
84112
* Checks if the current Next.js version uses Turbopack as the default bundler.
85113
* Starting from Next.js 15.6.0-canary.38, turbopack became the default for `next build`.
86114
*
@@ -144,4 +172,3 @@ export function detectActiveBundler(nextJsVersion: string | undefined): 'turbopa
144172
// Unlikely but at this point, we just assume webpack for older behavior
145173
return 'webpack';
146174
}
147-

packages/nextjs/test/config/util.test.ts

Lines changed: 116 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,122 @@ describe('util', () => {
9797
});
9898
});
9999

100+
describe('supportsNativeDebugIds', () => {
101+
describe('supported versions', () => {
102+
it.each([
103+
// Next.js 16+ stable versions
104+
['16.0.0', 'Next.js 16.0.0 stable'],
105+
['16.0.1', 'Next.js 16.0.1 stable'],
106+
['16.1.0', 'Next.js 16.1.0 stable'],
107+
['16.2.5', 'Next.js 16.2.5 stable'],
108+
109+
// Next.js 16+ pre-release versions
110+
['16.0.0-rc.1', 'Next.js 16.0.0-rc.1'],
111+
['16.0.0-canary.1', 'Next.js 16.0.0-canary.1'],
112+
['16.1.0-beta.2', 'Next.js 16.1.0-beta.2'],
113+
114+
// Next.js 17+
115+
['17.0.0', 'Next.js 17.0.0'],
116+
['18.0.0', 'Next.js 18.0.0'],
117+
['20.0.0', 'Next.js 20.0.0'],
118+
119+
// Next.js 15.6.0-canary.36+ (boundary case)
120+
['15.6.0-canary.36', 'Next.js 15.6.0-canary.36 (exact threshold)'],
121+
['15.6.0-canary.37', 'Next.js 15.6.0-canary.37'],
122+
['15.6.0-canary.38', 'Next.js 15.6.0-canary.38'],
123+
['15.6.0-canary.40', 'Next.js 15.6.0-canary.40'],
124+
['15.6.0-canary.100', 'Next.js 15.6.0-canary.100'],
125+
126+
// Next.js 15.7+ canary versions
127+
['15.7.0-canary.1', 'Next.js 15.7.0-canary.1'],
128+
['15.7.0-canary.50', 'Next.js 15.7.0-canary.50'],
129+
['15.8.0-canary.1', 'Next.js 15.8.0-canary.1'],
130+
['15.10.0-canary.1', 'Next.js 15.10.0-canary.1'],
131+
])('returns true for %s (%s)', version => {
132+
expect(util.supportsNativeDebugIds(version)).toBe(true);
133+
});
134+
});
135+
136+
describe('unsupported versions', () => {
137+
it.each([
138+
// Next.js 15.6.0-canary.35 and below
139+
['15.6.0-canary.35', 'Next.js 15.6.0-canary.35 (just below threshold)'],
140+
['15.6.0-canary.34', 'Next.js 15.6.0-canary.34'],
141+
['15.6.0-canary.0', 'Next.js 15.6.0-canary.0'],
142+
['15.6.0-canary.1', 'Next.js 15.6.0-canary.1'],
143+
144+
// Next.js 15.6.x stable releases (NOT canary)
145+
['15.6.0', 'Next.js 15.6.0 stable'],
146+
['15.6.1', 'Next.js 15.6.1 stable'],
147+
['15.6.2', 'Next.js 15.6.2 stable'],
148+
['15.6.10', 'Next.js 15.6.10 stable'],
149+
150+
// Next.js 15.6.x rc releases (NOT canary)
151+
['15.6.0-rc.1', 'Next.js 15.6.0-rc.1'],
152+
['15.6.0-rc.2', 'Next.js 15.6.0-rc.2'],
153+
154+
// Next.js 15.7+ stable releases (NOT canary)
155+
['15.7.0', 'Next.js 15.7.0 stable'],
156+
['15.8.0', 'Next.js 15.8.0 stable'],
157+
['15.10.0', 'Next.js 15.10.0 stable'],
158+
159+
// Next.js 15.7+ rc/beta releases (NOT canary)
160+
['15.7.0-rc.1', 'Next.js 15.7.0-rc.1'],
161+
['15.7.0-beta.1', 'Next.js 15.7.0-beta.1'],
162+
163+
// Next.js 15.5 and below (all versions)
164+
['15.5.0', 'Next.js 15.5.0'],
165+
['15.5.0-canary.100', 'Next.js 15.5.0-canary.100'],
166+
['15.4.1', 'Next.js 15.4.1'],
167+
['15.0.0', 'Next.js 15.0.0'],
168+
['15.0.0-canary.1', 'Next.js 15.0.0-canary.1'],
169+
170+
// Next.js 14.x and below
171+
['14.2.0', 'Next.js 14.2.0'],
172+
['14.0.0', 'Next.js 14.0.0'],
173+
['14.0.0-canary.50', 'Next.js 14.0.0-canary.50'],
174+
['13.5.0', 'Next.js 13.5.0'],
175+
['13.0.0', 'Next.js 13.0.0'],
176+
['12.0.0', 'Next.js 12.0.0'],
177+
])('returns false for %s (%s)', version => {
178+
expect(util.supportsNativeDebugIds(version)).toBe(false);
179+
});
180+
});
181+
182+
describe('edge cases', () => {
183+
it.each([
184+
['', 'empty string'],
185+
['invalid', 'invalid version string'],
186+
['15', 'missing minor and patch'],
187+
['15.6', 'missing patch'],
188+
['not.a.version', 'completely invalid'],
189+
['15.6.0-alpha.1', 'alpha prerelease (not canary)'],
190+
['15.6.0-beta.1', 'beta prerelease (not canary)'],
191+
])('returns false for %s (%s)', version => {
192+
expect(util.supportsNativeDebugIds(version)).toBe(false);
193+
});
194+
});
195+
196+
describe('canary number parsing edge cases', () => {
197+
it.each([
198+
['15.6.0-canary.', 'canary with no number'],
199+
['15.6.0-canary.abc', 'canary with non-numeric value'],
200+
['15.6.0-canary.35.extra', 'canary with extra segments'],
201+
])('handles malformed canary versions: %s (%s)', version => {
202+
// Should not throw, just return appropriate boolean
203+
expect(() => util.supportsNativeDebugIds(version)).not.toThrow();
204+
});
205+
206+
it('handles canary.36 exactly (boundary)', () => {
207+
expect(util.supportsNativeDebugIds('15.6.0-canary.36')).toBe(true);
208+
});
209+
210+
it('handles canary.35 exactly (boundary)', () => {
211+
expect(util.supportsNativeDebugIds('15.6.0-canary.35')).toBe(false);
212+
});
213+
});
214+
});
215+
100216
describe('isTurbopackDefaultForVersion', () => {
101217
describe('returns true for versions where turbopack is default', () => {
102218
it.each([

0 commit comments

Comments
 (0)