main
js 367 lines 14.6 KB
Raw
1 /**
2 * Copyright (c) Meta Platforms, Inc. and affiliates.
3 *
4 * This source code is licensed under the MIT license found in the
5 * LICENSE file in the root directory of this source tree.
6 *
7 * @flow
8 */
9 import type {
10 PreconnectOptions,
11 PreloadOptions,
12 PreloadModuleOptions,
13 PreinitOptions,
14 PreinitModuleOptions,
15 } from './ReactDOMTypes';
16
17 import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
18
19 import {
20 getCrossOriginString,
21 getCrossOriginStringAs,
22 } from 'react-dom-bindings/src/shared/crossOriginStrings';
23
24 export function prefetchDNS(href: string) {
25 if (__DEV__) {
26 if (typeof href !== 'string' || !href) {
27 console.error(
28 'ReactDOM.prefetchDNS(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.',
29 getValueDescriptorExpectingObjectForWarning(href),
30 );
31 } else if (arguments.length > 1) {
32 const options = arguments[1];
33 if (
34 typeof options === 'object' &&
35 options.hasOwnProperty('crossOrigin')
36 ) {
37 console.error(
38 '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`.',
39 getValueDescriptorExpectingEnumForWarning(options),
40 );
41 } else {
42 console.error(
43 '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`.',
44 getValueDescriptorExpectingEnumForWarning(options),
45 );
46 }
47 }
48 }
49 if (typeof href === 'string') {
50 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
51 .D(/* prefetchDNS */ href);
52 }
53 // We don't error because preconnect needs to be resilient to being called in a variety of scopes
54 // and the runtime may not be capable of responding. The function is optimistic and not critical
55 // so we favor silent bailout over warning or erroring.
56 }
57
58 export function preconnect(href: string, options?: ?PreconnectOptions) {
59 if (__DEV__) {
60 if (typeof href !== 'string' || !href) {
61 console.error(
62 'ReactDOM.preconnect(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.',
63 getValueDescriptorExpectingObjectForWarning(href),
64 );
65 } else if (options != null && typeof options !== 'object') {
66 console.error(
67 '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.',
68 getValueDescriptorExpectingEnumForWarning(options),
69 );
70 } else if (options != null && typeof options.crossOrigin !== 'string') {
71 console.error(
72 '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.',
73 getValueDescriptorExpectingObjectForWarning(options.crossOrigin),
74 );
75 }
76 }
77 if (typeof href === 'string') {
78 const crossOrigin = options
79 ? getCrossOriginString(options.crossOrigin)
80 : null;
81 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
82 .C(/* preconnect */ href, crossOrigin);
83 }
84 // We don't error because preconnect needs to be resilient to being called in a variety of scopes
85 // and the runtime may not be capable of responding. The function is optimistic and not critical
86 // so we favor silent bailout over warning or erroring.
87 }
88
89 export function preload(href: string, options: PreloadOptions) {
90 if (__DEV__) {
91 let encountered = '';
92 if (typeof href !== 'string' || !href) {
93 encountered += ` The \`href\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
94 href,
95 )}.`;
96 }
97 if (options == null || typeof options !== 'object') {
98 encountered += ` The \`options\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
99 options,
100 )}.`;
101 } else if (typeof options.as !== 'string' || !options.as) {
102 encountered += ` The \`as\` option encountered was ${getValueDescriptorExpectingObjectForWarning(
103 options.as,
104 )}.`;
105 }
106 if (encountered) {
107 console.error(
108 'ReactDOM.preload(): Expected two arguments, a non-empty `href` string and an `options` object with an `as` property valid for a `<link rel="preload" as="..." />` tag.%s',
109 encountered,
110 );
111 }
112 }
113 if (
114 typeof href === 'string' &&
115 // We check existence because we cannot enforce this function is actually called with the stated type
116 typeof options === 'object' &&
117 // $FlowFixMe[invalid-compare]
118 options !== null &&
119 typeof options.as === 'string'
120 ) {
121 const as = options.as;
122 const crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);
123 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
124 .L(/* preload */ href, as, {
125 crossOrigin,
126 integrity:
127 typeof options.integrity === 'string' ? options.integrity : undefined,
128 nonce: typeof options.nonce === 'string' ? options.nonce : undefined,
129 type: typeof options.type === 'string' ? options.type : undefined,
130 fetchPriority:
131 typeof options.fetchPriority === 'string'
132 ? options.fetchPriority
133 : undefined,
134 referrerPolicy:
135 typeof options.referrerPolicy === 'string'
136 ? options.referrerPolicy
137 : undefined,
138 imageSrcSet:
139 typeof options.imageSrcSet === 'string'
140 ? options.imageSrcSet
141 : undefined,
142 imageSizes:
143 typeof options.imageSizes === 'string'
144 ? options.imageSizes
145 : undefined,
146 media: typeof options.media === 'string' ? options.media : undefined,
147 });
148 }
149 // We don't error because preload needs to be resilient to being called in a variety of scopes
150 // and the runtime may not be capable of responding. The function is optimistic and not critical
151 // so we favor silent bailout over warning or erroring.
152 }
153
154 export function preloadModule(href: string, options?: ?PreloadModuleOptions) {
155 if (__DEV__) {
156 let encountered = '';
157 if (typeof href !== 'string' || !href) {
158 encountered += ` The \`href\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
159 href,
160 )}.`;
161 }
162 if (options !== undefined && typeof options !== 'object') {
163 encountered += ` The \`options\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
164 options,
165 )}.`;
166 } else if (options && 'as' in options && typeof options.as !== 'string') {
167 encountered += ` The \`as\` option encountered was ${getValueDescriptorExpectingObjectForWarning(
168 options.as,
169 )}.`;
170 }
171 if (encountered) {
172 console.error(
173 'ReactDOM.preloadModule(): Expected two arguments, a non-empty `href` string and, optionally, an `options` object with an `as` property valid for a `<link rel="modulepreload" as="..." />` tag.%s',
174 encountered,
175 );
176 }
177 }
178 if (typeof href === 'string') {
179 if (options) {
180 const crossOrigin = getCrossOriginStringAs(
181 options.as,
182 options.crossOrigin,
183 );
184 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
185 .m(/* preloadModule */ href, {
186 as:
187 typeof options.as === 'string' && options.as !== 'script'
188 ? options.as
189 : undefined,
190 crossOrigin,
191 integrity:
192 typeof options.integrity === 'string'
193 ? options.integrity
194 : undefined,
195 nonce: typeof options.nonce === 'string' ? options.nonce : undefined,
196 fetchPriority:
197 typeof options.fetchPriority === 'string'
198 ? options.fetchPriority
199 : undefined,
200 });
201 } else {
202 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
203 .m(/* preloadModule */ href);
204 }
205 }
206 // We don't error because preload needs to be resilient to being called in a variety of scopes
207 // and the runtime may not be capable of responding. The function is optimistic and not critical
208 // so we favor silent bailout over warning or erroring.
209 }
210
211 export function preinit(href: string, options: PreinitOptions) {
212 if (__DEV__) {
213 if (typeof href !== 'string' || !href) {
214 console.error(
215 'ReactDOM.preinit(): Expected the `href` argument (first) to be a non-empty string but encountered %s instead.',
216 getValueDescriptorExpectingObjectForWarning(href),
217 );
218 } else if (options == null || typeof options !== 'object') {
219 console.error(
220 'ReactDOM.preinit(): Expected the `options` argument (second) to be an object with an `as` property describing the type of resource to be preinitialized but encountered %s instead.',
221 getValueDescriptorExpectingEnumForWarning(options),
222 );
223 } else if (options.as !== 'style' && options.as !== 'script') {
224 console.error(
225 'ReactDOM.preinit(): Expected the `as` property in the `options` argument (second) to contain a valid value describing the type of resource to be preinitialized but encountered %s instead. Valid values for `as` are "style" and "script".',
226 getValueDescriptorExpectingEnumForWarning(options.as),
227 );
228 }
229 }
230 if (typeof href === 'string' && options && typeof options.as === 'string') {
231 const as = options.as;
232 const crossOrigin = getCrossOriginStringAs(as, options.crossOrigin);
233 const integrity =
234 typeof options.integrity === 'string' ? options.integrity : undefined;
235 const fetchPriority =
236 typeof options.fetchPriority === 'string'
237 ? options.fetchPriority
238 : undefined;
239 if (as === 'style') {
240 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
241 .S(
242 /* preinitStyle */
243 href,
244 typeof options.precedence === 'string'
245 ? options.precedence
246 : undefined,
247 {
248 crossOrigin,
249 integrity,
250 fetchPriority,
251 },
252 );
253 } else if (as === 'script') {
254 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
255 .X(/* preinitScript */ href, {
256 crossOrigin,
257 integrity,
258 fetchPriority,
259 nonce: typeof options.nonce === 'string' ? options.nonce : undefined,
260 });
261 }
262 }
263 // We don't error because preinit needs to be resilient to being called in a variety of scopes
264 // and the runtime may not be capable of responding. The function is optimistic and not critical
265 // so we favor silent bailout over warning or erroring.
266 }
267
268 export function preinitModule(href: string, options?: ?PreinitModuleOptions) {
269 if (__DEV__) {
270 let encountered = '';
271 if (typeof href !== 'string' || !href) {
272 encountered += ` The \`href\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
273 href,
274 )}.`;
275 }
276 if (options !== undefined && typeof options !== 'object') {
277 encountered += ` The \`options\` argument encountered was ${getValueDescriptorExpectingObjectForWarning(
278 options,
279 )}.`;
280 } else if (options && 'as' in options && options.as !== 'script') {
281 encountered += ` The \`as\` option encountered was ${getValueDescriptorExpectingEnumForWarning(
282 options.as,
283 )}.`;
284 }
285 if (encountered) {
286 console.error(
287 'ReactDOM.preinitModule(): Expected up to two arguments, a non-empty `href` string and, optionally, an `options` object with a valid `as` property.%s',
288 encountered,
289 );
290 } else {
291 const as =
292 options && typeof options.as === 'string' ? options.as : 'script';
293 switch (as) {
294 case 'script': {
295 break;
296 }
297
298 // We have an invalid as type and need to warn
299 default: {
300 const typeOfAs = getValueDescriptorExpectingEnumForWarning(as);
301 console.error(
302 'ReactDOM.preinitModule(): Currently the only supported "as" type for this function is "script"' +
303 ' but received "%s" instead. This warning was generated for `href` "%s". In the future other' +
304 ' module types will be supported, aligning with the import-attributes proposal. Learn more here:' +
305 ' (https://github.com/tc39/proposal-import-attributes)',
306 typeOfAs,
307 href,
308 );
309 }
310 }
311 }
312 }
313 if (typeof href === 'string') {
314 if (typeof options === 'object' && options !== null) {
315 if (options.as == null || options.as === 'script') {
316 const crossOrigin = getCrossOriginStringAs(
317 options.as,
318 options.crossOrigin,
319 );
320 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
321 .M(/* preinitModuleScript */ href, {
322 crossOrigin,
323 integrity:
324 typeof options.integrity === 'string'
325 ? options.integrity
326 : undefined,
327 nonce:
328 typeof options.nonce === 'string' ? options.nonce : undefined,
329 fetchPriority:
330 typeof options.fetchPriority === 'string'
331 ? options.fetchPriority
332 : undefined,
333 });
334 }
335 } else if (options == null) {
336 ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */
337 .M(/* preinitModuleScript */ href);
338 }
339 }
340 // We don't error because preinit needs to be resilient to being called in a variety of scopes
341 // and the runtime may not be capable of responding. The function is optimistic and not critical
342 // so we favor silent bailout over warning or erroring.
343 }
344
345 function getValueDescriptorExpectingObjectForWarning(thing: any): string {
346 return thing === null
347 ? '`null`'
348 : thing === undefined
349 ? '`undefined`'
350 : thing === ''
351 ? 'an empty string'
352 : `something with type "${typeof thing}"`;
353 }
354
355 function getValueDescriptorExpectingEnumForWarning(thing: any): string {
356 return thing === null
357 ? '`null`'
358 : thing === undefined
359 ? '`undefined`'
360 : thing === ''
361 ? 'an empty string'
362 : typeof thing === 'string'
363 ? JSON.stringify(thing)
364 : typeof thing === 'number'
365 ? '`' + thing + '`'
366 : `something with type "${typeof thing}"`;
367 }