Skip to content

Commit 323a9c9

Browse files
authored
patch react via recast instead of string replacements (#78916)
In #78838, we've seen that patching react via string replacements is problematic, because we accidentally replaced a `setTimeout` in a string literal (emitted into HTML by Fizz). This PR converts most of our react patching to use `recast` instead. This also lets us be smarter about how we convert `setTimeout` to `setTimeoutOrImmediate` -- i'm removing the duration arg, and potentially preserving `setTimeout(fn, 0, ...ARGS_FOR_CALLBACK)`
1 parent 0625d0d commit 323a9c9

15 files changed

+361
-230
lines changed

packages/next/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -304,6 +304,7 @@
304304
"querystring-es3": "0.2.1",
305305
"raw-body": "2.4.1",
306306
"react-refresh": "0.12.0",
307+
"recast": "0.23.11",
307308
"regenerator-runtime": "0.13.4",
308309
"sass-loader": "15.0.0",
309310
"schema-utils2": "npm:[email protected]",

packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server.edge.development.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
"use strict";
3636
"production" !== process.env.NODE_ENV &&
3737
(function () {
38+
// This is a patch added by Next.js
39+
const setTimeoutOrImmediate =
40+
typeof globalThis["set" + "Immediate"] === "function" &&
41+
// edge runtime sandbox defines a stub for setImmediate
42+
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
43+
// but it's made non-enumerable, so we can detect it
44+
globalThis.propertyIsEnumerable("setImmediate")
45+
? globalThis["set" + "Immediate"]
46+
: (callback, ...args) => setTimeout(callback, 0, ...args);
3847
function styleReplacer(match, prefix, s, suffix) {
3948
return "" + prefix + ("s" === s ? "\\73 " : "\\53 ") + suffix;
4049
}
@@ -4903,7 +4912,7 @@
49034912
})
49044913
: setTimeoutOrImmediate(function () {
49054914
return performWork(request);
4906-
}, 0));
4915+
}));
49074916
}
49084917
function createSuspenseBoundary(
49094918
request,
@@ -8473,7 +8482,7 @@
84738482
request
84748483
)
84758484
: enqueueEarlyPreloadsAfterInitialWork(request));
8476-
}, 0);
8485+
});
84778486
}
84788487
function enqueueEarlyPreloadsAfterInitialWork(request) {
84798488
safelyEmitEarlyPreloads(request, 0 === request.pendingRootTasks);
@@ -8488,7 +8497,7 @@
84888497
destination
84898498
? flushCompletedQueues(request, destination)
84908499
: (request.flushScheduled = !1);
8491-
}, 0));
8500+
}));
84928501
}
84938502
function startFlowing(request, destination) {
84948503
if (13 === request.status)
@@ -10392,16 +10401,5 @@
1039210401
startWork(request);
1039310402
});
1039410403
};
10395-
10396-
// This is a patch added by Next.js
10397-
const setTimeoutOrImmediate =
10398-
typeof globalThis['set' + 'Immediate'] === 'function' &&
10399-
// edge runtime sandbox defines a stub for setImmediate
10400-
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
10401-
// but it's made non-enumerable, so we can detect it
10402-
globalThis.propertyIsEnumerable('setImmediate')
10403-
? globalThis['set' + 'Immediate']
10404-
: setTimeout;
10405-
1040610404
exports.version = "19.2.0-experimental-197d6a04-20250424";
1040710405
})();

packages/next/src/compiled/react-dom-experimental/cjs/react-dom-server.edge.production.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
// This is a patch added by Next.js
2+
const setTimeoutOrImmediate =
3+
typeof globalThis["set" + "Immediate"] === "function" &&
4+
// edge runtime sandbox defines a stub for setImmediate
5+
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
6+
// but it's made non-enumerable, so we can detect it
7+
globalThis.propertyIsEnumerable("setImmediate")
8+
? globalThis["set" + "Immediate"]
9+
: (callback, ...args) => setTimeout(callback, 0, ...args);
110
/**
211
* @license React
312
* react-dom-server.edge.production.js
@@ -4411,7 +4420,7 @@ function pingTask(request, task) {
44114420
})
44124421
: setTimeoutOrImmediate(function () {
44134422
return performWork(request);
4414-
}, 0));
4423+
}));
44154424
}
44164425
function createSuspenseBoundary(
44174426
request,
@@ -6936,7 +6945,7 @@ function startWork(request) {
69366945
request
69376946
)
69386947
: enqueueEarlyPreloadsAfterInitialWork(request));
6939-
}, 0);
6948+
});
69406949
}
69416950
function enqueueEarlyPreloadsAfterInitialWork(request) {
69426951
safelyEmitEarlyPreloads(request, 0 === request.pendingRootTasks);
@@ -6951,7 +6960,7 @@ function enqueueFlush(request) {
69516960
destination
69526961
? flushCompletedQueues(request, destination)
69536962
: (request.flushScheduled = !1);
6954-
}, 0));
6963+
}));
69556964
}
69566965
function startFlowing(request, destination) {
69576966
if (13 === request.status)
@@ -7305,15 +7314,4 @@ exports.resumeAndPrerender = function (children, postponedState, options) {
73057314
startWork(request);
73067315
});
73077316
};
7308-
7309-
// This is a patch added by Next.js
7310-
const setTimeoutOrImmediate =
7311-
typeof globalThis['set' + 'Immediate'] === 'function' &&
7312-
// edge runtime sandbox defines a stub for setImmediate
7313-
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
7314-
// but it's made non-enumerable, so we can detect it
7315-
globalThis.propertyIsEnumerable('setImmediate')
7316-
? globalThis['set' + 'Immediate']
7317-
: setTimeout;
7318-
73197317
exports.version = "19.2.0-experimental-197d6a04-20250424";

packages/next/src/compiled/react-dom/cjs/react-dom-server.edge.development.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,15 @@
3535
"use strict";
3636
"production" !== process.env.NODE_ENV &&
3737
(function () {
38+
// This is a patch added by Next.js
39+
const setTimeoutOrImmediate =
40+
typeof globalThis["set" + "Immediate"] === "function" &&
41+
// edge runtime sandbox defines a stub for setImmediate
42+
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
43+
// but it's made non-enumerable, so we can detect it
44+
globalThis.propertyIsEnumerable("setImmediate")
45+
? globalThis["set" + "Immediate"]
46+
: (callback, ...args) => setTimeout(callback, 0, ...args);
3847
function styleReplacer(match, prefix, s, suffix) {
3948
return "" + prefix + ("s" === s ? "\\73 " : "\\53 ") + suffix;
4049
}
@@ -4565,7 +4574,7 @@
45654574
})
45664575
: setTimeoutOrImmediate(function () {
45674576
return performWork(request);
4568-
}, 0));
4577+
}));
45694578
}
45704579
function createSuspenseBoundary(
45714580
request,
@@ -7704,7 +7713,7 @@
77047713
request
77057714
)
77067715
: enqueueEarlyPreloadsAfterInitialWork(request));
7707-
}, 0);
7716+
});
77087717
}
77097718
function enqueueEarlyPreloadsAfterInitialWork(request) {
77107719
safelyEmitEarlyPreloads(request, 0 === request.pendingRootTasks);
@@ -7719,7 +7728,7 @@
77197728
destination
77207729
? flushCompletedQueues(request, destination)
77217730
: (request.flushScheduled = !1);
7722-
}, 0));
7731+
}));
77237732
}
77247733
function startFlowing(request, destination) {
77257734
if (13 === request.status)
@@ -9439,16 +9448,5 @@
94399448
startWork(request);
94409449
});
94419450
};
9442-
9443-
// This is a patch added by Next.js
9444-
const setTimeoutOrImmediate =
9445-
typeof globalThis['set' + 'Immediate'] === 'function' &&
9446-
// edge runtime sandbox defines a stub for setImmediate
9447-
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
9448-
// but it's made non-enumerable, so we can detect it
9449-
globalThis.propertyIsEnumerable('setImmediate')
9450-
? globalThis['set' + 'Immediate']
9451-
: setTimeout;
9452-
94539451
exports.version = "19.2.0-canary-197d6a04-20250424";
94549452
})();

packages/next/src/compiled/react-dom/cjs/react-dom-server.edge.production.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
// This is a patch added by Next.js
2+
const setTimeoutOrImmediate =
3+
typeof globalThis["set" + "Immediate"] === "function" &&
4+
// edge runtime sandbox defines a stub for setImmediate
5+
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
6+
// but it's made non-enumerable, so we can detect it
7+
globalThis.propertyIsEnumerable("setImmediate")
8+
? globalThis["set" + "Immediate"]
9+
: (callback, ...args) => setTimeout(callback, 0, ...args);
110
/**
211
* @license React
312
* react-dom-server.edge.production.js
@@ -4078,7 +4087,7 @@ function pingTask(request, task) {
40784087
})
40794088
: setTimeoutOrImmediate(function () {
40804089
return performWork(request);
4081-
}, 0));
4090+
}));
40824091
}
40834092
function createSuspenseBoundary(
40844093
request,
@@ -6262,7 +6271,7 @@ function startWork(request) {
62626271
request
62636272
)
62646273
: enqueueEarlyPreloadsAfterInitialWork(request));
6265-
}, 0);
6274+
});
62666275
}
62676276
function enqueueEarlyPreloadsAfterInitialWork(request) {
62686277
safelyEmitEarlyPreloads(request, 0 === request.pendingRootTasks);
@@ -6277,7 +6286,7 @@ function enqueueFlush(request) {
62776286
destination
62786287
? flushCompletedQueues(request, destination)
62796288
: (request.flushScheduled = !1);
6280-
}, 0));
6289+
}));
62816290
}
62826291
function startFlowing(request, destination) {
62836292
if (13 === request.status)
@@ -6468,15 +6477,4 @@ exports.renderToReadableStream = function (children, options) {
64686477
startWork(request);
64696478
});
64706479
};
6471-
6472-
// This is a patch added by Next.js
6473-
const setTimeoutOrImmediate =
6474-
typeof globalThis['set' + 'Immediate'] === 'function' &&
6475-
// edge runtime sandbox defines a stub for setImmediate
6476-
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
6477-
// but it's made non-enumerable, so we can detect it
6478-
globalThis.propertyIsEnumerable('setImmediate')
6479-
? globalThis['set' + 'Immediate']
6480-
: setTimeout;
6481-
64826480
exports.version = "19.2.0-canary-197d6a04-20250424";

packages/next/src/compiled/react-server-dom-turbopack-experimental/cjs/react-server-dom-turbopack-server.edge.development.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,15 @@
1111
"use strict";
1212
"production" !== process.env.NODE_ENV &&
1313
(function () {
14+
// This is a patch added by Next.js
15+
const setTimeoutOrImmediate =
16+
typeof globalThis["set" + "Immediate"] === "function" &&
17+
// edge runtime sandbox defines a stub for setImmediate
18+
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
19+
// but it's made non-enumerable, so we can detect it
20+
globalThis.propertyIsEnumerable("setImmediate")
21+
? globalThis["set" + "Immediate"]
22+
: (callback, ...args) => setTimeout(callback, 0, ...args);
1423
function voidHandler() {}
1524
function getIteratorFn(maybeIterable) {
1625
if (null === maybeIterable || "object" !== typeof maybeIterable)
@@ -1445,7 +1454,7 @@
14451454
})
14461455
: setTimeoutOrImmediate(function () {
14471456
return performWork(request);
1448-
}, 0));
1457+
}));
14491458
}
14501459
function createTask(
14511460
request,
@@ -2826,7 +2835,7 @@
28262835
});
28272836
setTimeoutOrImmediate(function () {
28282837
request.status === OPENING && (request.status = 11);
2829-
}, 0);
2838+
});
28302839
}
28312840
function enqueueFlush(request) {
28322841
!1 === request.flushScheduled &&
@@ -2837,7 +2846,7 @@
28372846
request.flushScheduled = !1;
28382847
var destination = request.destination;
28392848
destination && flushCompletedChunks(request, destination);
2840-
}, 0));
2849+
}));
28412850
}
28422851
function callOnAllReadyIfReady(request) {
28432852
if (
@@ -4303,17 +4312,6 @@
43034312
bind: { value: bind, configurable: !0 }
43044313
});
43054314
};
4306-
4307-
// This is a patch added by Next.js
4308-
const setTimeoutOrImmediate =
4309-
typeof globalThis['set' + 'Immediate'] === 'function' &&
4310-
// edge runtime sandbox defines a stub for setImmediate
4311-
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
4312-
// but it's made non-enumerable, so we can detect it
4313-
globalThis.propertyIsEnumerable('setImmediate')
4314-
? globalThis['set' + 'Immediate']
4315-
: setTimeout;
4316-
43174315
exports.renderToReadableStream = function (model, turbopackMap, options) {
43184316
var request = createRequest(
43194317
model,

packages/next/src/compiled/react-server-dom-turbopack-experimental/cjs/react-server-dom-turbopack-server.edge.production.js

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,12 @@
1+
// This is a patch added by Next.js
2+
const setTimeoutOrImmediate =
3+
typeof globalThis["set" + "Immediate"] === "function" &&
4+
// edge runtime sandbox defines a stub for setImmediate
5+
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
6+
// but it's made non-enumerable, so we can detect it
7+
globalThis.propertyIsEnumerable("setImmediate")
8+
? globalThis["set" + "Immediate"]
9+
: (callback, ...args) => setTimeout(callback, 0, ...args);
110
/**
211
* @license React
312
* react-server-dom-turbopack-server.edge.production.js
@@ -1144,7 +1153,7 @@ function pingTask(request, task) {
11441153
})
11451154
: setTimeoutOrImmediate(function () {
11461155
return performWork(request);
1147-
}, 0));
1156+
}));
11481157
}
11491158
function createTask(request, model, keyPath, implicitSlot, abortSet) {
11501159
request.pendingChunks++;
@@ -1949,7 +1958,7 @@ function startWork(request) {
19491958
});
19501959
setTimeoutOrImmediate(function () {
19511960
10 === request.status && (request.status = 11);
1952-
}, 0);
1961+
});
19531962
}
19541963
function enqueueFlush(request) {
19551964
!1 === request.flushScheduled &&
@@ -1960,7 +1969,7 @@ function enqueueFlush(request) {
19601969
request.flushScheduled = !1;
19611970
var destination = request.destination;
19621971
destination && flushCompletedChunks(request, destination);
1963-
}, 0));
1972+
}));
19641973
}
19651974
function callOnAllReadyIfReady(request) {
19661975
if (0 === request.abortableTasks.size && 0 === request.abortListeners.size)
@@ -2881,17 +2890,6 @@ exports.registerServerReference = function (reference, id, exportName) {
28812890
bind: { value: bind, configurable: !0 }
28822891
});
28832892
};
2884-
2885-
// This is a patch added by Next.js
2886-
const setTimeoutOrImmediate =
2887-
typeof globalThis['set' + 'Immediate'] === 'function' &&
2888-
// edge runtime sandbox defines a stub for setImmediate
2889-
// (see 'addStub' in packages/next/src/server/web/sandbox/context.ts)
2890-
// but it's made non-enumerable, so we can detect it
2891-
globalThis.propertyIsEnumerable('setImmediate')
2892-
? globalThis['set' + 'Immediate']
2893-
: setTimeout;
2894-
28952893
exports.renderToReadableStream = function (model, turbopackMap, options) {
28962894
var request = new RequestInstance(
28972895
20,

0 commit comments

Comments
 (0)