· 4 years ago · May 11, 2021, 04:26 PM
1import type {DOMEventName} from './DOMEventNames';
2
3import {enableCreateEventHandleAPI} from 'shared/ReactFeatureFlags';
4
5export const allNativeEvents: Set<DOMEventName> = new Set();
6
7if (enableCreateEventHandleAPI) {
8 allNativeEvents.add('beforeblur');
9 allNativeEvents.add('afterblur');
10}
11
12/**
13 * Mapping from registration name to event name
14 */
15export const registrationNameDependencies = {};
16
17/**
18 * Mapping from lowercase registration names to the properly cased version,
19 * used to warn in the case of missing event handlers. Available
20 * only in __DEV__.
21 * @type {Object}
22 */
23export const possibleRegistrationNames = __DEV__ ? {} : (null: any);
24// Trust the developer to only use possibleRegistrationNames in __DEV__
25
26export function registerTwoPhaseEvent(
27 registrationName: string,
28 dependencies: Array<DOMEventName>,
29): void {
30 registerDirectEvent(registrationName, dependencies);
31 registerDirectEvent(registrationName + 'Capture', dependencies);
32}
33
34export function registerDirectEvent(
35 registrationName: string,
36 dependencies: Array<DOMEventName>,
37) {
38 if (__DEV__) {
39 if (registrationNameDependencies[registrationName]) {
40 console.error(
41 'EventRegistry: More than one plugin attempted to publish the same ' +
42 'registration name, `%s`.',
43 registrationName,
44 );
45 }
46 }
47
48 registrationNameDependencies[registrationName] = dependencies;
49
50 if (__DEV__) {
51 const lowerCasedName = registrationName.toLowerCase();
52 possibleRegistrationNames[lowerCasedName] = registrationName;
53
54 if (registrationName === 'onDoubleClick') {
55 possibleRegistrationNames.ondblclick = registrationName;
56 }
57 }
58
59 for (let i = 0; i < dependencies.length; i++) {
60 allNativeEvents.add(dependencies[i]);
61 }
62}
63
64const matchHtmlRegExp = /["'&<>]/;
65
66/**
67 * Escapes special characters and HTML entities in a given html string.
68 *
69 * @param {string} string HTML string to escape for later insertion
70 * @return {string}
71 * @public
72 */
73
74function escapeHtml(string) {
75 const str = '' + string;
76 const match = matchHtmlRegExp.exec(str);
77
78 if (!match) {
79 return str;
80 }
81
82 let escape;
83 let html = '';
84 let index;
85 let lastIndex = 0;
86
87 for (index = match.index; index < str.length; index++) {
88 switch (str.charCodeAt(index)) {
89 case 34: // "
90 escape = '"';
91 break;
92 case 38: // &
93 escape = '&';
94 break;
95 case 39: // '
96 escape = '''; // modified from escape-html; used to be '''
97 break;
98 case 60: // <
99 escape = '<';
100 break;
101 case 62: // >
102 escape = '>';
103 break;
104 default:
105 continue;
106 }
107
108 if (lastIndex !== index) {
109 html += str.substring(lastIndex, index);
110 }
111
112 lastIndex = index + 1;
113 html += escape;
114 }
115
116 return lastIndex !== index ? html + str.substring(lastIndex, index) : html;
117}
118// end code copied and modified from escape-html
119
120/**
121 * Escapes text to prevent scripting attacks.
122 *
123 * @param {*} text Text value to escape.
124 * @return {string} An escaped string.
125 */
126function escapeTextForBrowser(text) {
127 if (typeof text === 'boolean' || typeof text === 'number') {
128 // this shortcircuit helps perf for types that we know will never have
129 // special characters, especially given that this function is used often
130 // for numeric dom ids.
131 return '' + text;
132 }
133 return escapeHtml(text);
134}
135
136export default escapeTextForBrowser;
137
138import type {ReactNodeList} from 'shared/ReactTypes';
139import type {Container} from './ReactDOMHostConfig';
140
141import {
142 findDOMNode,
143 render,
144 hydrate,
145 unstable_renderSubtreeIntoContainer,
146 unmountComponentAtNode,
147} from './ReactDOMLegacy';
148import {createRoot, isValidContainer} from './ReactDOMRoot';
149import {createEventHandle} from './ReactDOMEventHandle';
150
151import {
152 batchedEventUpdates,
153 batchedUpdates,
154 discreteUpdates,
155 flushDiscreteUpdates,
156 flushSync,
157 flushControlled,
158 injectIntoDevTools,
159 flushPassiveEffects,
160 IsThisRendererActing,
161 attemptSynchronousHydration,
162 attemptDiscreteHydration,
163 attemptContinuousHydration,
164 attemptHydrationAtCurrentPriority,
165} from 'react-reconciler/src/ReactFiberReconciler';
166import {
167 runWithPriority,
168 getCurrentUpdatePriority,
169} from 'react-reconciler/src/ReactEventPriorities';
170import {createPortal as createPortalImpl} from 'react-reconciler/src/ReactPortal';
171import {canUseDOM} from 'shared/ExecutionEnvironment';
172import ReactVersion from 'shared/ReactVersion';
173import invariant from 'shared/invariant';
174import {
175 warnUnstableRenderSubtreeIntoContainer,
176 enableNewReconciler,
177} from 'shared/ReactFeatureFlags';
178
179import {
180 getInstanceFromNode,
181 getNodeFromInstance,
182 getFiberCurrentPropsFromNode,
183 getClosestInstanceFromNode,
184} from './ReactDOMComponentTree';
185import {restoreControlledState} from './ReactDOMComponent';
186import {
187 setAttemptSynchronousHydration,
188 setAttemptDiscreteHydration,
189 setAttemptContinuousHydration,
190 setAttemptHydrationAtCurrentPriority,
191 queueExplicitHydrationTarget,
192 setGetCurrentUpdatePriority,
193 setAttemptHydrationAtPriority,
194} from '../events/ReactDOMEventReplaying';
195import {setBatchingImplementation} from '../events/ReactDOMUpdateBatching';
196import {
197 setRestoreImplementation,
198 enqueueStateRestore,
199 restoreStateIfNeeded,
200} from '../events/ReactDOMControlledComponent';
201
202setAttemptSynchronousHydration(attemptSynchronousHydration);
203setAttemptDiscreteHydration(attemptDiscreteHydration);
204setAttemptContinuousHydration(attemptContinuousHydration);
205setAttemptHydrationAtCurrentPriority(attemptHydrationAtCurrentPriority);
206setGetCurrentUpdatePriority(getCurrentUpdatePriority);
207setAttemptHydrationAtPriority(runWithPriority);
208
209let didWarnAboutUnstableCreatePortal = false;
210let didWarnAboutUnstableRenderSubtreeIntoContainer = false;
211
212if (__DEV__) {
213 if (
214 typeof Map !== 'function' ||
215 // $FlowIssue Flow incorrectly thinks Map has no prototype
216 Map.prototype == null ||
217 typeof Map.prototype.forEach !== 'function' ||
218 typeof Set !== 'function' ||
219 // $FlowIssue Flow incorrectly thinks Set has no prototype
220 Set.prototype == null ||
221 typeof Set.prototype.clear !== 'function' ||
222 typeof Set.prototype.forEach !== 'function'
223 ) {
224 console.error(
225 'React depends on Map and Set built-in types. Make sure that you load a ' +
226 'polyfill in older browsers. https://reactjs.org/link/react-polyfills',
227 );
228 }
229}
230
231setRestoreImplementation(restoreControlledState);
232setBatchingImplementation(
233 batchedUpdates,
234 discreteUpdates,
235 flushDiscreteUpdates,
236 batchedEventUpdates,
237);
238
239function createPortal(
240 children: ReactNodeList,
241 container: Container,
242 key: ?string = null,
243): React$Portal {
244 invariant(
245 isValidContainer(container),
246 'Target container is not a DOM element.',
247 );
248 // TODO: pass ReactDOM portal implementation as third argument
249 // $FlowFixMe The Flow type is opaque but there's no way to actually create it.
250 return createPortalImpl(children, container, null, key);
251}
252
253function scheduleHydration(target: Node) {
254 if (target) {
255 queueExplicitHydrationTarget(target);
256 }
257}
258
259function renderSubtreeIntoContainer(
260 parentComponent: React$Component<any, any>,
261 element: React$Element<any>,
262 containerNode: Container,
263 callback: ?Function,
264) {
265 if (__DEV__) {
266 if (
267 warnUnstableRenderSubtreeIntoContainer &&
268 !didWarnAboutUnstableRenderSubtreeIntoContainer
269 ) {
270 didWarnAboutUnstableRenderSubtreeIntoContainer = true;
271 console.warn(
272 'ReactDOM.unstable_renderSubtreeIntoContainer() is deprecated ' +
273 'and will be removed in a future major release. Consider using ' +
274 'React Portals instead.',
275 );
276 }
277 }
278 return unstable_renderSubtreeIntoContainer(
279 parentComponent,
280 element,
281 containerNode,
282 callback,
283 );
284}
285
286function unstable_createPortal(
287 children: ReactNodeList,
288 container: Container,
289 key: ?string = null,
290) {
291 if (__DEV__) {
292 if (!didWarnAboutUnstableCreatePortal) {
293 didWarnAboutUnstableCreatePortal = true;
294 console.warn(
295 'The ReactDOM.unstable_createPortal() alias has been deprecated, ' +
296 'and will be removed in React 18+. Update your code to use ' +
297 'ReactDOM.createPortal() instead. It has the exact same API, ' +
298 'but without the "unstable_" prefix.',
299 );
300 }
301 }
302 return createPortal(children, container, key);
303}
304
305const Internals = {
306 // Keep in sync with ReactTestUtils.js, and ReactTestUtilsAct.js.
307 // This is an array for better minification.
308 Events: [
309 getInstanceFromNode,
310 getNodeFromInstance,
311 getFiberCurrentPropsFromNode,
312 enqueueStateRestore,
313 restoreStateIfNeeded,
314 flushPassiveEffects,
315 // TODO: This is related to `act`, not events. Move to separate key?
316 IsThisRendererActing,
317 ],
318};
319
320export {
321 createPortal,
322 batchedUpdates as unstable_batchedUpdates,
323 flushSync,
324 Internals as __SECRET_INTERNALS_DO_NOT_USE_OR_YOU_WILL_BE_FIRED,
325 ReactVersion as version,
326 // Disabled behind disableLegacyReactDOMAPIs
327 findDOMNode,
328 hydrate,
329 render,
330 unmountComponentAtNode,
331 // exposeConcurrentModeAPIs
332 createRoot,
333 flushControlled as unstable_flushControlled,
334 scheduleHydration as unstable_scheduleHydration,
335 // Disabled behind disableUnstableRenderSubtreeIntoContainer
336 renderSubtreeIntoContainer as unstable_renderSubtreeIntoContainer,
337 // Disabled behind disableUnstableCreatePortal
338 // Temporary alias since we already shipped React 16 RC with it.
339 // TODO: remove in React 18.
340 unstable_createPortal,
341 // enableCreateEventHandleAPI
342 createEventHandle as unstable_createEventHandle,
343 // TODO: Remove this once callers migrate to alternatives.
344 // This should only be used by React internals.
345 runWithPriority as unstable_runWithPriority,
346};
347
348const foundDevTools = injectIntoDevTools({
349 findFiberByHostInstance: getClosestInstanceFromNode,
350 bundleType: __DEV__ ? 1 : 0,
351 version: ReactVersion,
352 rendererPackageName: 'react-dom',
353});
354
355if (__DEV__) {
356 if (!foundDevTools && canUseDOM && window.top === window.self) {
357 // If we're in Chrome or Firefox, provide a download link if not installed.
358 if (
359 (navigator.userAgent.indexOf('Chrome') > -1 &&
360 navigator.userAgent.indexOf('Edge') === -1) ||
361 navigator.userAgent.indexOf('Firefox') > -1
362 ) {
363 const protocol = window.location.protocol;
364 // Don't warn in exotic cases like chrome-extension://.
365 if (/^(https?|file):$/.test(protocol)) {
366 // eslint-disable-next-line react-internal/no-production-logging
367 console.info(
368 '%cDownload the React DevTools ' +
369 'for a better development experience: ' +
370 'https://reactjs.org/link/react-devtools' +
371 (protocol === 'file:'
372 ? '\nYou might need to use a local HTTP server (instead of file://): ' +
373 'https://reactjs.org/link/react-devtools-faq'
374 : ''),
375 'font-weight:bold',
376 );
377 }
378 }
379 }
380}
381
382export const unstable_isNewReconciler = enableNewReconciler;
383
384
385import type {Wakeable, Thenable} from 'shared/ReactTypes';
386
387import {REACT_LAZY_TYPE} from 'shared/ReactSymbols';
388
389const Uninitialized = -1;
390const Pending = 0;
391const Resolved = 1;
392const Rejected = 2;
393
394type UninitializedPayload<T> = {
395 _status: -1,
396 _result: () => Thenable<{default: T, ...}>,
397};
398
399type PendingPayload = {
400 _status: 0,
401 _result: Wakeable,
402};
403
404type ResolvedPayload<T> = {
405 _status: 1,
406 _result: T,
407};
408
409type RejectedPayload = {
410 _status: 2,
411 _result: mixed,
412};
413
414type Payload<T> =
415 | UninitializedPayload<T>
416 | PendingPayload
417 | ResolvedPayload<T>
418 | RejectedPayload;
419
420export type LazyComponent<T, P> = {
421 $$typeof: Symbol | number,
422 _payload: P,
423 _init: (payload: P) => T,
424};
425
426function lazyInitializer<T>(payload: Payload<T>): T {
427 if (payload._status === Uninitialized) {
428 const ctor = payload._result;
429 const thenable = ctor();
430 // Transition to the next state.
431 const pending: PendingPayload = (payload: any);
432 pending._status = Pending;
433 pending._result = thenable;
434 thenable.then(
435 moduleObject => {
436 if (payload._status === Pending) {
437 const defaultExport = moduleObject.default;
438 if (__DEV__) {
439 if (defaultExport === undefined) {
440 console.error(
441 'lazy: Expected the result of a dynamic import() call. ' +
442 'Instead received: %s\n\nYour code should look like: \n ' +
443 // Break up imports to avoid accidentally parsing them as dependencies.
444 'const MyComponent = lazy(() => imp' +
445 "ort('./MyComponent'))",
446 moduleObject,
447 );
448 }
449 }
450 // Transition to the next state.
451 const resolved: ResolvedPayload<T> = (payload: any);
452 resolved._status = Resolved;
453 resolved._result = defaultExport;
454 }
455 },
456 error => {
457 if (payload._status === Pending) {
458 // Transition to the next state.
459 const rejected: RejectedPayload = (payload: any);
460 rejected._status = Rejected;
461 rejected._result = error;
462 }
463 },
464 );
465 }
466 if (payload._status === Resolved) {
467 return payload._result;
468 } else {
469 throw payload._result;
470 }
471}
472
473export function lazy<T>(
474 ctor: () => Thenable<{default: T, ...}>,
475): LazyComponent<T, Payload<T>> {
476 const payload: Payload<T> = {
477 // We use these fields to store the result.
478 _status: -1,
479 _result: ctor,
480 };
481
482 const lazyType: LazyComponent<T, Payload<T>> = {
483 $$typeof: REACT_LAZY_TYPE,
484 _payload: payload,
485 _init: lazyInitializer,
486 };
487
488 if (__DEV__) {
489 // In production, this would just set it on the object.
490 let defaultProps;
491 let propTypes;
492 // $FlowFixMe
493 Object.defineProperties(lazyType, {
494 defaultProps: {
495 configurable: true,
496 get() {
497 return defaultProps;
498 },
499 set(newDefaultProps) {
500 console.error(
501 'React.lazy(...): It is not supported to assign `defaultProps` to ' +
502 'a lazy component import. Either specify them where the component ' +
503 'is defined, or create a wrapping component around it.',
504 );
505 defaultProps = newDefaultProps;
506 // Match production behavior more closely:
507 // $FlowFixMe
508 Object.defineProperty(lazyType, 'defaultProps', {
509 enumerable: true,
510 });
511 },
512 },
513 propTypes: {
514 configurable: true,
515 get() {
516 return propTypes;
517 },
518 set(newPropTypes) {
519 console.error(
520 'React.lazy(...): It is not supported to assign `propTypes` to ' +
521 'a lazy component import. Either specify them where the component ' +
522 'is defined, or create a wrapping component around it.',
523 );
524 propTypes = newPropTypes;
525 // Match production behavior more closely:
526 // $FlowFixMe
527 Object.defineProperty(lazyType, 'propTypes', {
528 enumerable: true,
529 });
530 },
531 },
532 });
533
534import {REACT_PROVIDER_TYPE, REACT_CONTEXT_TYPE} from 'shared/ReactSymbols';
535
536import type {ReactContext} from 'shared/ReactTypes';
537
538export function createContext<T>(defaultValue: T): ReactContext<T> {
539 // TODO: Second argument used to be an optional `calculateChangedBits`
540 // function. Warn to reserve for future use?
541
542 const context: ReactContext<T> = {
543 $$typeof: REACT_CONTEXT_TYPE,
544 // As a workaround to support multiple concurrent renderers, we categorize
545 // some renderers as primary and others as secondary. We only expect
546 // there to be two concurrent renderers at most: React Native (primary) and
547 // Fabric (secondary); React DOM (primary) and React ART (secondary).
548 // Secondary renderers store their context values on separate fields.
549 _currentValue: defaultValue,
550 _currentValue2: defaultValue,
551 // Used to track how many concurrent renderers this context currently
552 // supports within in a single renderer. Such as parallel server rendering.
553 _threadCount: 0,
554 // These are circular
555 Provider: (null: any),
556 Consumer: (null: any),
557 };
558
559 context.Provider = {
560 $$typeof: REACT_PROVIDER_TYPE,
561 _context: context,
562 };
563
564 let hasWarnedAboutUsingNestedContextConsumers = false;
565 let hasWarnedAboutUsingConsumerProvider = false;
566 let hasWarnedAboutDisplayNameOnConsumer = false;
567
568 if (__DEV__) {
569 // A separate object, but proxies back to the original context object for
570 // backwards compatibility. It has a different $$typeof, so we can properly
571 // warn for the incorrect usage of Context as a Consumer.
572 const Consumer = {
573 $$typeof: REACT_CONTEXT_TYPE,
574 _context: context,
575 };
576 // $FlowFixMe: Flow complains about not setting a value, which is intentional here
577 Object.defineProperties(Consumer, {
578 Provider: {
579 get() {
580 if (!hasWarnedAboutUsingConsumerProvider) {
581 hasWarnedAboutUsingConsumerProvider = true;
582 console.error(
583 'Rendering <Context.Consumer.Provider> is not supported and will be removed in ' +
584 'a future major release. Did you mean to render <Context.Provider> instead?',
585 );
586 }
587 return context.Provider;
588 },
589 set(_Provider) {
590 context.Provider = _Provider;
591 },
592 },
593 _currentValue: {
594 get() {
595 return context._currentValue;
596 },
597 set(_currentValue) {
598 context._currentValue = _currentValue;
599 },
600 },
601 _currentValue2: {
602 get() {
603 return context._currentValue2;
604 },
605 set(_currentValue2) {
606 context._currentValue2 = _currentValue2;
607 },
608 },
609 _threadCount: {
610 get() {
611 return context._threadCount;
612 },
613 set(_threadCount) {
614 context._threadCount = _threadCount;
615 },
616 },
617 Consumer: {
618 get() {
619 if (!hasWarnedAboutUsingNestedContextConsumers) {
620 hasWarnedAboutUsingNestedContextConsumers = true;
621 console.error(
622 'Rendering <Context.Consumer.Consumer> is not supported and will be removed in ' +
623 'a future major release. Did you mean to render <Context.Consumer> instead?',
624 );
625 }
626 return context.Consumer;
627 },
628 },
629 displayName: {
630 get() {
631 return context.displayName;
632 },
633 set(displayName) {
634 if (!hasWarnedAboutDisplayNameOnConsumer) {
635 console.warn(
636 'Setting `displayName` on Context.Consumer has no effect. ' +
637 "You should set it directly on the context with Context.displayName = '%s'.",
638 displayName,
639 );
640 hasWarnedAboutDisplayNameOnConsumer = true;
641 }
642 },
643 },
644 });
645 // $FlowFixMe: Flow complains about missing properties because it doesn't understand defineProperty
646 context.Consumer = Consumer;
647 } else {
648 context.Consumer = context;
649 }
650
651 if (__DEV__) {
652 context._currentRenderer = null;
653 context._currentRenderer2 = null;
654 }
655 }
656
657 return lazyType;
658}