Skip to content

Commit 5fc3a28

Browse files
committed
support crossOrigin for preconnect
1 parent 7ae8685 commit 5fc3a28

File tree

3 files changed

+125
-32
lines changed

3 files changed

+125
-32
lines changed

packages/react-dom-bindings/src/client/ReactDOMFloatClient.js

Lines changed: 40 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -156,16 +156,23 @@ function getDocumentFromRoot(root: HoistableRoot): Document {
156156
return root.ownerDocument || root;
157157
}
158158

159-
function preconnectAs(rel: 'preconnect' | 'dns-prefetch', href: string) {
159+
function preconnectAs(
160+
rel: 'preconnect' | 'dns-prefetch',
161+
crossOrigin: null | '' | 'use-credentials',
162+
href: string,
163+
) {
160164
const ownerDocument = getDocumentForPreloads();
161165
if (typeof href === 'string' && href && ownerDocument) {
162166
const limitedEscapedHref =
163167
escapeSelectorAttributeValueInsideDoubleQuotes(href);
164-
const key = `link[rel="${rel}"][href="${limitedEscapedHref}"]`;
168+
let key = `link[rel="${rel}"][href="${limitedEscapedHref}"]`;
169+
if (typeof crossOrigin === 'string') {
170+
key += `[crossorigin="${crossOrigin}"]`;
171+
}
165172
if (!preconnectsSet.has(key)) {
166173
preconnectsSet.add(key);
167174

168-
const preconnectProps = {rel, href};
175+
const preconnectProps = {rel, crossOrigin, href};
169176
if (null === ownerDocument.querySelector(key)) {
170177
const preloadInstance = createElement(
171178
'link',
@@ -192,33 +199,54 @@ function prefetchDNS(href: string, options?: mixed) {
192199
getValueDescriptorExpectingObjectForWarning(href),
193200
);
194201
} else if (options != null) {
195-
console.error(
196-
'ReactDOM.prefetchDNS(): Expected only one argument (href) but encountered a second argument, %s, instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
197-
getValueDescriptorExpectingEnumForWarning(options),
198-
);
202+
if (
203+
typeof options === 'object' &&
204+
options.hasOwnProperty('crossOrigin')
205+
) {
206+
console.error(
207+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered %s as a second argument instead. This argument is reserved for future options and is currently disallowed. It looks like the you are attempting to set a crossOrigin property for this DNS lookup hint. Browsers do not perform DNS queries using CORS and setting this attribute on the resource hint has no effect. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
208+
getValueDescriptorExpectingEnumForWarning(options),
209+
);
210+
} else {
211+
console.error(
212+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered %s as a second argument instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
213+
getValueDescriptorExpectingEnumForWarning(options),
214+
);
215+
}
199216
}
200217
}
201-
preconnectAs('dns-prefetch', href);
218+
preconnectAs('dns-prefetch', null, href);
202219
}
203220

204221
// --------------------------------------
205222
// ReactDOM.preconnect
206223
// --------------------------------------
207-
function preconnect(href: string, options?: mixed) {
224+
function preconnect(href: string, options?: {crossOrigin?: string}) {
208225
if (__DEV__) {
209226
if (typeof href !== 'string' || !href) {
210227
console.error(
211228
'ReactDOM.preconnect(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.',
212229
getValueDescriptorExpectingObjectForWarning(href),
213230
);
214-
} else if (options != null) {
231+
} else if (options != null && typeof options !== 'object') {
215232
console.error(
216-
'ReactDOM.preconnect(): Expected only one argument (href) but encountered a second argument, %s, instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.preconnect() with just a single string argument, `href`.',
233+
'ReactDOM.preconnect(): Expected the `options` argument (second) to be an object but encountered %s instead. The only supported option at this time is `crossOrigin` which accepts a string.',
217234
getValueDescriptorExpectingEnumForWarning(options),
218235
);
236+
} else if (options != null && typeof options.crossOrigin !== 'string') {
237+
console.error(
238+
'ReactDOM.preconnect(): Expected the `crossOrigin` option (second argument) to be a string but encountered %s instead. Try removing this option or passing a string value instead.',
239+
getValueDescriptorExpectingObjectForWarning(options.crossOrigin),
240+
);
219241
}
220242
}
221-
preconnectAs('preconnect', href);
243+
const crossOrigin =
244+
options == null || typeof options.crossOrigin !== 'string'
245+
? null
246+
: options.crossOrigin === 'use-credentials'
247+
? 'use-credentials'
248+
: '';
249+
preconnectAs('preconnect', crossOrigin, href);
222250
}
223251

224252
// --------------------------------------

packages/react-dom-bindings/src/server/ReactDOMServerFormatConfig.js

Lines changed: 33 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -4243,10 +4243,20 @@ export function prefetchDNS(href: string, options?: mixed) {
42434243
getValueDescriptorExpectingObjectForWarning(href),
42444244
);
42454245
} else if (options != null) {
4246-
console.error(
4247-
'ReactDOM.prefetchDNS(): Expected only one argument (href) but encountered a second argument, %s, instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
4248-
getValueDescriptorExpectingEnumForWarning(options),
4249-
);
4246+
if (
4247+
typeof options === 'object' &&
4248+
options.hasOwnProperty('crossOrigin')
4249+
) {
4250+
console.error(
4251+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered %s as a second argument instead. This argument is reserved for future options and is currently disallowed. It looks like the you are attempting to set a crossOrigin property for this DNS lookup hint. Browsers do not perform DNS queries using CORS and setting this attribute on the resource hint has no effect. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
4252+
getValueDescriptorExpectingEnumForWarning(options),
4253+
);
4254+
} else {
4255+
console.error(
4256+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered %s as a second argument instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
4257+
getValueDescriptorExpectingEnumForWarning(options),
4258+
);
4259+
}
42504260
}
42514261
}
42524262

@@ -4270,7 +4280,7 @@ export function prefetchDNS(href: string, options?: mixed) {
42704280
}
42714281
}
42724282

4273-
export function preconnect(href: string, options?: mixed) {
4283+
export function preconnect(href: string, options?: {crossOrigin?: string}) {
42744284
if (!currentResources) {
42754285
// While we expect that preconnect calls are primarily going to be observed
42764286
// during render because effects and events don't run on the server it is
@@ -4287,16 +4297,30 @@ export function preconnect(href: string, options?: mixed) {
42874297
'ReactDOM.preconnect(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.',
42884298
getValueDescriptorExpectingObjectForWarning(href),
42894299
);
4290-
} else if (options != null) {
4300+
} else if (options != null && typeof options !== 'object') {
42914301
console.error(
4292-
'ReactDOM.preconnect(): Expected only one argument (href) but encountered a second argument, %s, instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.preconnect() with just a single string argument, `href`.',
4302+
'ReactDOM.preconnect(): Expected the `options` argument (second) to be an object but encountered %s instead. The only supported option at this time is `crossOrigin` which accepts a string.',
42934303
getValueDescriptorExpectingEnumForWarning(options),
42944304
);
4305+
} else if (options != null && typeof options.crossOrigin !== 'string') {
4306+
console.error(
4307+
'ReactDOM.preconnect(): Expected the `crossOrigin` option (second argument) to be a string but encountered %s instead. Try removing this option or passing a string value instead.',
4308+
getValueDescriptorExpectingObjectForWarning(options.crossOrigin),
4309+
);
42954310
}
42964311
}
42974312

42984313
if (typeof href === 'string' && href) {
4299-
const key = getResourceKey('preconnect', href);
4314+
const crossOrigin =
4315+
options == null || typeof options.crossOrigin !== 'string'
4316+
? null
4317+
: options.crossOrigin === 'use-credentials'
4318+
? 'use-credentials'
4319+
: '';
4320+
4321+
const key = `[preconnect][${
4322+
crossOrigin === null ? 'null' : crossOrigin
4323+
}]${href}`;
43004324
let resource = resources.preconnectsMap.get(key);
43014325
if (!resource) {
43024326
resource = {
@@ -4308,7 +4332,7 @@ export function preconnect(href: string, options?: mixed) {
43084332
resources.preconnectsMap.set(key, resource);
43094333
pushLinkImpl(
43104334
resource.chunks,
4311-
({href, rel: 'preconnect'}: PreconnectProps),
4335+
({rel: 'preconnect', href, crossOrigin}: PreconnectProps),
43124336
);
43134337
}
43144338
resources.preconnects.add(resource);

packages/react-dom/src/__tests__/ReactDOMFloat-test.js

Lines changed: 52 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -2300,16 +2300,23 @@ body {
23002300
function App({url}) {
23012301
ReactDOM.prefetchDNS(url);
23022302
ReactDOM.prefetchDNS(url);
2303+
ReactDOM.prefetchDNS(url, {});
2304+
ReactDOM.prefetchDNS(url, {crossOrigin: 'use-credentials'});
23032305
return (
23042306
<html>
23052307
<body>hello world</body>
23062308
</html>
23072309
);
23082310
}
23092311

2310-
await actIntoEmptyDocument(() => {
2311-
renderToPipeableStream(<App url="foo" />).pipe(writable);
2312-
});
2312+
await expect(async () => {
2313+
await actIntoEmptyDocument(() => {
2314+
renderToPipeableStream(<App url="foo" />).pipe(writable);
2315+
});
2316+
}).toErrorDev([
2317+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered something with type "object" as a second argument instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
2318+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered something with type "object" as a second argument instead. This argument is reserved for future options and is currently disallowed. It looks like the you are attempting to set a crossOrigin property for this DNS lookup hint. Browsers do not perform DNS queries using CORS and setting this attribute on the resource hint has no effect. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
2319+
]);
23132320

23142321
expect(getMeaningfulChildren(document)).toEqual(
23152322
<html>
@@ -2321,7 +2328,12 @@ body {
23212328
);
23222329

23232330
const root = ReactDOMClient.hydrateRoot(document, <App url="foo" />);
2324-
expect(Scheduler).toFlushWithoutYielding();
2331+
expect(() => {
2332+
expect(Scheduler).toFlushWithoutYielding();
2333+
}).toErrorDev([
2334+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered something with type "object" as a second argument instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
2335+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered something with type "object" as a second argument instead. This argument is reserved for future options and is currently disallowed. It looks like the you are attempting to set a crossOrigin property for this DNS lookup hint. Browsers do not perform DNS queries using CORS and setting this attribute on the resource hint has no effect. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
2336+
]);
23252337
expect(getMeaningfulChildren(document)).toEqual(
23262338
<html>
23272339
<head>
@@ -2332,7 +2344,12 @@ body {
23322344
);
23332345

23342346
root.render(<App url="bar" />);
2335-
expect(Scheduler).toFlushWithoutYielding();
2347+
expect(() => {
2348+
expect(Scheduler).toFlushWithoutYielding();
2349+
}).toErrorDev([
2350+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered something with type "object" as a second argument instead. This argument is reserved for future options and is currently disallowed. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
2351+
'ReactDOM.prefetchDNS(): Expected only one argument, `href`, but encountered something with type "object" as a second argument instead. This argument is reserved for future options and is currently disallowed. It looks like the you are attempting to set a crossOrigin property for this DNS lookup hint. Browsers do not perform DNS queries using CORS and setting this attribute on the resource hint has no effect. Try calling ReactDOM.prefetchDNS() with just a single string argument, `href`.',
2352+
]);
23362353
expect(getMeaningfulChildren(document)).toEqual(
23372354
<html>
23382355
<head>
@@ -2345,49 +2362,73 @@ body {
23452362
});
23462363
});
23472364

2348-
describe('ReactDOM.preconnect(href)', () => {
2365+
describe('ReactDOM.preconnect(href, { crossOrigin })', () => {
23492366
it('creates a preconnect resource when called', async () => {
23502367
function App({url}) {
23512368
ReactDOM.preconnect(url);
23522369
ReactDOM.preconnect(url);
2370+
ReactDOM.preconnect(url, {crossOrigin: true});
2371+
ReactDOM.preconnect(url, {crossOrigin: ''});
2372+
ReactDOM.preconnect(url, {crossOrigin: 'anonymous'});
2373+
ReactDOM.preconnect(url, {crossOrigin: 'use-credentials'});
23532374
return (
23542375
<html>
23552376
<body>hello world</body>
23562377
</html>
23572378
);
23582379
}
23592380

2360-
await actIntoEmptyDocument(() => {
2361-
renderToPipeableStream(<App url="foo" />).pipe(writable);
2362-
});
2381+
await expect(async () => {
2382+
await actIntoEmptyDocument(() => {
2383+
renderToPipeableStream(<App url="foo" />).pipe(writable);
2384+
});
2385+
}).toErrorDev(
2386+
'ReactDOM.preconnect(): Expected the `crossOrigin` option (second argument) to be a string but encountered something with type "boolean" instead. Try removing this option or passing a string value instead.',
2387+
);
23632388

23642389
expect(getMeaningfulChildren(document)).toEqual(
23652390
<html>
23662391
<head>
23672392
<link rel="preconnect" href="foo" />
2393+
<link rel="preconnect" href="foo" crossorigin="" />
2394+
<link rel="preconnect" href="foo" crossorigin="use-credentials" />
23682395
</head>
23692396
<body>hello world</body>
23702397
</html>,
23712398
);
23722399

23732400
const root = ReactDOMClient.hydrateRoot(document, <App url="foo" />);
2374-
expect(Scheduler).toFlushWithoutYielding();
2401+
expect(() => {
2402+
expect(Scheduler).toFlushWithoutYielding();
2403+
}).toErrorDev(
2404+
'ReactDOM.preconnect(): Expected the `crossOrigin` option (second argument) to be a string but encountered something with type "boolean" instead. Try removing this option or passing a string value instead.',
2405+
);
23752406
expect(getMeaningfulChildren(document)).toEqual(
23762407
<html>
23772408
<head>
23782409
<link rel="preconnect" href="foo" />
2410+
<link rel="preconnect" href="foo" crossorigin="" />
2411+
<link rel="preconnect" href="foo" crossorigin="use-credentials" />
23792412
</head>
23802413
<body>hello world</body>
23812414
</html>,
23822415
);
23832416

23842417
root.render(<App url="bar" />);
2385-
expect(Scheduler).toFlushWithoutYielding();
2418+
expect(() => {
2419+
expect(Scheduler).toFlushWithoutYielding();
2420+
}).toErrorDev(
2421+
'ReactDOM.preconnect(): Expected the `crossOrigin` option (second argument) to be a string but encountered something with type "boolean" instead. Try removing this option or passing a string value instead.',
2422+
);
23862423
expect(getMeaningfulChildren(document)).toEqual(
23872424
<html>
23882425
<head>
23892426
<link rel="preconnect" href="foo" />
2427+
<link rel="preconnect" href="foo" crossorigin="" />
2428+
<link rel="preconnect" href="foo" crossorigin="use-credentials" />
23902429
<link rel="preconnect" href="bar" />
2430+
<link rel="preconnect" href="bar" crossorigin="" />
2431+
<link rel="preconnect" href="bar" crossorigin="use-credentials" />
23912432
</head>
23922433
<body>hello world</body>
23932434
</html>,

0 commit comments

Comments
 (0)