mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
See [Flow changelog](https://github.com/facebook/flow/blob/main/Changelog.md) for changes in this version.
266 lines
6.9 KiB
JavaScript
266 lines
6.9 KiB
JavaScript
/**
|
|
* Copyright (c) Meta Platforms, Inc. and affiliates.
|
|
*
|
|
* This source code is licensed under the MIT license found in the
|
|
* LICENSE file in the root directory of this source tree.
|
|
*
|
|
* @flow
|
|
*/
|
|
|
|
import type {
|
|
CrossOriginEnum,
|
|
PreloadImplOptions,
|
|
PreloadModuleImplOptions,
|
|
PreinitStyleOptions,
|
|
PreinitScriptOptions,
|
|
PreinitModuleScriptOptions,
|
|
} from 'react-dom/src/shared/ReactDOMTypes';
|
|
|
|
import {
|
|
emitHint,
|
|
getHints,
|
|
resolveRequest,
|
|
} from 'react-server/src/ReactFlightServer';
|
|
|
|
import ReactDOMSharedInternals from 'shared/ReactDOMSharedInternals';
|
|
|
|
const previousDispatcher =
|
|
ReactDOMSharedInternals.d; /* ReactDOMCurrentDispatcher */
|
|
ReactDOMSharedInternals.d /* ReactDOMCurrentDispatcher */ = {
|
|
f /* flushSyncWork */: previousDispatcher.f /* flushSyncWork */,
|
|
r /* requestFormReset */: previousDispatcher.r /* requestFormReset */,
|
|
D /* prefetchDNS */: prefetchDNS,
|
|
C /* preconnect */: preconnect,
|
|
L /* preload */: preload,
|
|
m /* preloadModule */: preloadModule,
|
|
X /* preinitScript */: preinitScript,
|
|
S /* preinitStyle */: preinitStyle,
|
|
M /* preinitModuleScript */: preinitModuleScript,
|
|
};
|
|
|
|
function prefetchDNS(href: string) {
|
|
if (typeof href === 'string' && href) {
|
|
const request = resolveRequest();
|
|
if (request) {
|
|
const hints = getHints(request);
|
|
const key = 'D|' + href;
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
hints.add(key);
|
|
emitHint(request, 'D', href);
|
|
} else {
|
|
previousDispatcher.D(/* prefetchDNS */ href);
|
|
}
|
|
}
|
|
}
|
|
|
|
function preconnect(href: string, crossOrigin?: ?CrossOriginEnum) {
|
|
if (typeof href === 'string') {
|
|
const request = resolveRequest();
|
|
if (request) {
|
|
const hints = getHints(request);
|
|
|
|
const key = `C|${crossOrigin == null ? 'null' : crossOrigin}|${href}`;
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
hints.add(key);
|
|
if (typeof crossOrigin === 'string') {
|
|
emitHint(request, 'C', [href, crossOrigin]);
|
|
} else {
|
|
emitHint(request, 'C', href);
|
|
}
|
|
} else {
|
|
previousDispatcher.C(/* preconnect */ href, crossOrigin);
|
|
}
|
|
}
|
|
}
|
|
|
|
function preload(href: string, as: string, options?: ?PreloadImplOptions) {
|
|
if (typeof href === 'string') {
|
|
const request = resolveRequest();
|
|
if (request) {
|
|
const hints = getHints(request);
|
|
let key = 'L';
|
|
if (as === 'image' && options) {
|
|
key += getImagePreloadKey(
|
|
href,
|
|
options.imageSrcSet,
|
|
options.imageSizes,
|
|
);
|
|
} else {
|
|
key += `[${as}]${href}`;
|
|
}
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
hints.add(key);
|
|
|
|
const trimmed = trimOptions(options);
|
|
if (trimmed) {
|
|
emitHint(request, 'L', [href, as, trimmed]);
|
|
} else {
|
|
emitHint(request, 'L', [href, as]);
|
|
}
|
|
} else {
|
|
previousDispatcher.L(/* preload */ href, as, options);
|
|
}
|
|
}
|
|
}
|
|
|
|
function preloadModule(href: string, options?: ?PreloadModuleImplOptions) {
|
|
if (typeof href === 'string') {
|
|
const request = resolveRequest();
|
|
if (request) {
|
|
const hints = getHints(request);
|
|
const key = 'm|' + href;
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
hints.add(key);
|
|
|
|
const trimmed = trimOptions(options);
|
|
if (trimmed) {
|
|
return emitHint(request, 'm', [href, trimmed]);
|
|
} else {
|
|
return emitHint(request, 'm', href);
|
|
}
|
|
} else {
|
|
previousDispatcher.m(/* preloadModule */ href, options);
|
|
}
|
|
}
|
|
}
|
|
|
|
function preinitStyle(
|
|
href: string,
|
|
precedence: ?string,
|
|
options?: ?PreinitStyleOptions,
|
|
) {
|
|
if (typeof href === 'string') {
|
|
const request = resolveRequest();
|
|
if (request) {
|
|
const hints = getHints(request);
|
|
const key = 'S|' + href;
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
hints.add(key);
|
|
|
|
const trimmed = trimOptions(options);
|
|
if (trimmed) {
|
|
return emitHint(request, 'S', [
|
|
href,
|
|
typeof precedence === 'string' ? precedence : 0,
|
|
trimmed,
|
|
]);
|
|
} else if (typeof precedence === 'string') {
|
|
return emitHint(request, 'S', [href, precedence]);
|
|
} else {
|
|
return emitHint(request, 'S', href);
|
|
}
|
|
} else {
|
|
previousDispatcher.S(/* preinitStyle */ href, precedence, options);
|
|
}
|
|
}
|
|
}
|
|
|
|
function preinitScript(src: string, options?: ?PreinitScriptOptions) {
|
|
if (typeof src === 'string') {
|
|
const request = resolveRequest();
|
|
if (request) {
|
|
const hints = getHints(request);
|
|
const key = 'X|' + src;
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
hints.add(key);
|
|
|
|
const trimmed = trimOptions(options);
|
|
if (trimmed) {
|
|
return emitHint(request, 'X', [src, trimmed]);
|
|
} else {
|
|
return emitHint(request, 'X', src);
|
|
}
|
|
} else {
|
|
previousDispatcher.X(/* preinitScript */ src, options);
|
|
}
|
|
}
|
|
}
|
|
|
|
function preinitModuleScript(
|
|
src: string,
|
|
options?: ?PreinitModuleScriptOptions,
|
|
) {
|
|
if (typeof src === 'string') {
|
|
const request = resolveRequest();
|
|
if (request) {
|
|
const hints = getHints(request);
|
|
const key = 'M|' + src;
|
|
if (hints.has(key)) {
|
|
// duplicate hint
|
|
return;
|
|
}
|
|
hints.add(key);
|
|
|
|
const trimmed = trimOptions(options);
|
|
if (trimmed) {
|
|
return emitHint(request, 'M', [src, trimmed]);
|
|
} else {
|
|
return emitHint(request, 'M', src);
|
|
}
|
|
} else {
|
|
previousDispatcher.M(/* preinitModuleScript */ src, options);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Flight normally encodes undefined as a special character however for directive option
|
|
// arguments we don't want to send unnecessary keys and bloat the payload so we create a
|
|
// trimmed object which omits any keys with null or undefined values.
|
|
// This is only typesafe because these option objects have entirely optional fields where
|
|
// null and undefined represent the same thing as no property.
|
|
function trimOptions<
|
|
T:
|
|
| PreloadImplOptions
|
|
| PreloadModuleImplOptions
|
|
| PreinitStyleOptions
|
|
| PreinitScriptOptions
|
|
| PreinitModuleScriptOptions,
|
|
>(options: ?T): ?T {
|
|
if (options == null) return null;
|
|
let hasProperties = false;
|
|
const trimmed: T = ({}: any);
|
|
for (const key in options) {
|
|
// $FlowFixMe[invalid-computed-prop]
|
|
if (options[key] != null) {
|
|
hasProperties = true;
|
|
(trimmed: any)[key] = options[key];
|
|
}
|
|
}
|
|
return hasProperties ? trimmed : null;
|
|
}
|
|
|
|
function getImagePreloadKey(
|
|
href: string,
|
|
imageSrcSet: ?string,
|
|
imageSizes: ?string,
|
|
) {
|
|
let uniquePart = '';
|
|
if (typeof imageSrcSet === 'string' && imageSrcSet !== '') {
|
|
uniquePart += '[' + imageSrcSet + ']';
|
|
if (typeof imageSizes === 'string') {
|
|
uniquePart += '[' + imageSizes + ']';
|
|
}
|
|
} else {
|
|
uniquePart += '[][]' + href;
|
|
}
|
|
return `[image]${uniquePart}`;
|
|
}
|