Avoid meta programming to initialize functions in module scope (#26388)

I'm trying to get rid of all meta programming in the module scope so
that closure can do a better job figuring out cyclic dependencies and
ability to reorder.

This is converting a lot of the patterns that assign functions
conditionally to using function declarations instead.

```
let fn;
if (__DEV__) {
  fn = function() {
    ...
  };
}
```
->
```
function fn() {
  if (__DEV__) {
    ...
  }
}
```
This commit is contained in:
Sebastian Markbåge 2023-03-14 21:00:22 -04:00 committed by GitHub
parent 21aee59e45
commit d310d654a7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
24 changed files with 611 additions and 635 deletions

View File

@ -13,7 +13,7 @@
* Change environment support for PointerEvent.
*/
const emptyFunction = function () {};
function emptyFunction() {}
export function hasPointerEvent() {
return global != null && global.PointerEvent != null;

View File

@ -94,15 +94,8 @@ const HTML = '__html';
let warnedUnknownTags: {
[key: string]: boolean,
};
let validatePropertiesInDevelopment;
let warnForPropDifference;
let warnForExtraAttributes;
let warnForInvalidEventListener;
let canDiffStyleForHydrationWarning;
let normalizeHTML;
if (__DEV__) {
warnedUnknownTags = {
// There are working polyfills for <dialog>. Let people use it.
@ -115,15 +108,6 @@ if (__DEV__) {
webview: true,
};
validatePropertiesInDevelopment = function (type: string, props: any) {
validateARIAProperties(type, props);
validateInputProperties(type, props);
validateUnknownProperties(type, props, {
registrationNameDependencies,
possibleRegistrationNames,
});
};
// IE 11 parses & normalizes the style attribute as opposed to other
// browsers. It adds spaces and sorts the properties in some
// non-alphabetical order. Handling that would require sorting CSS
@ -133,12 +117,25 @@ if (__DEV__) {
// in that browser completely in favor of doing all that work.
// See https://github.com/facebook/react/issues/11807
canDiffStyleForHydrationWarning = canUseDOM && !document.documentMode;
}
warnForPropDifference = function (
propName: string,
serverValue: mixed,
clientValue: mixed,
) {
function validatePropertiesInDevelopment(type: string, props: any) {
if (__DEV__) {
validateARIAProperties(type, props);
validateInputProperties(type, props);
validateUnknownProperties(type, props, {
registrationNameDependencies,
possibleRegistrationNames,
});
}
}
function warnForPropDifference(
propName: string,
serverValue: mixed,
clientValue: mixed,
) {
if (__DEV__) {
if (didWarnInvalidHydration) {
return;
}
@ -156,9 +153,11 @@ if (__DEV__) {
JSON.stringify(normalizedServerValue),
JSON.stringify(normalizedClientValue),
);
};
}
}
warnForExtraAttributes = function (attributeNames: Set<string>) {
function warnForExtraAttributes(attributeNames: Set<string>) {
if (__DEV__) {
if (didWarnInvalidHydration) {
return;
}
@ -168,12 +167,11 @@ if (__DEV__) {
names.push(name);
});
console.error('Extra attributes from the server: %s', names);
};
}
}
warnForInvalidEventListener = function (
registrationName: string,
listener: any,
) {
function warnForInvalidEventListener(registrationName: string, listener: any) {
if (__DEV__) {
if (listener === false) {
console.error(
'Expected `%s` listener to be a function, instead got `false`.\n\n' +
@ -190,11 +188,13 @@ if (__DEV__) {
typeof listener,
);
}
};
}
}
// Parse the HTML and read it back to normalize the HTML string so that it
// can be used for comparison.
normalizeHTML = function (parent: Element, html: string) {
// Parse the HTML and read it back to normalize the HTML string so that it
// can be used for comparison.
function normalizeHTML(parent: Element, html: string) {
if (__DEV__) {
// We could have created a separate document here to avoid
// re-initializing custom elements if they exist. But this breaks
// how <noscript> is being handled. So we use the same document.
@ -208,7 +208,7 @@ if (__DEV__) {
);
testElement.innerHTML = html;
return testElement.innerHTML;
};
}
}
// HTML parsing normalizes CR and CRLF to LF.

View File

@ -1,25 +0,0 @@
/**
* 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.
*/
/* globals MSApp */
/**
* Create a function which has 'unsafe' privileges (required by windows8 apps)
*/
const createMicrosoftUnsafeLocalFunction = function (func) {
if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) {
return function (arg0, arg1, arg2, arg3) {
MSApp.execUnsafeLocalFunction(function () {
return func(arg0, arg1, arg2, arg3);
});
};
} else {
return func;
}
};
export default createMicrosoftUnsafeLocalFunction;

View File

@ -7,24 +7,15 @@
* @flow
*/
/* globals MSApp */
import {SVG_NAMESPACE} from './DOMNamespaces';
import createMicrosoftUnsafeLocalFunction from './createMicrosoftUnsafeLocalFunction';
import {enableTrustedTypesIntegration} from 'shared/ReactFeatureFlags';
// SVG temp container for IE lacking innerHTML
let reusableSVGContainer: HTMLElement;
/**
* Set the innerHTML property of a node
*
* @param {DOMElement} node
* @param {string} html
* @internal
*/
const setInnerHTML: (
node: Element,
html: {valueOf(): {toString(): string, ...}, ...},
) => void = createMicrosoftUnsafeLocalFunction(function (
function setInnerHTMLImpl(
node: Element,
html: {valueOf(): {toString(): string, ...}, ...},
): void {
@ -66,6 +57,26 @@ const setInnerHTML: (
}
}
node.innerHTML = (html: any);
});
}
let setInnerHTML: (
node: Element,
html: {valueOf(): {toString(): string, ...}, ...},
) => void = setInnerHTMLImpl;
// $FlowFixMe[cannot-resolve-name]
if (typeof MSApp !== 'undefined' && MSApp.execUnsafeLocalFunction) {
/**
* Create a function which has 'unsafe' privileges (required by windows8 apps)
*/
setInnerHTML = function (
node: Element,
html: {valueOf(): {toString(): string, ...}, ...},
): void {
// $FlowFixMe[cannot-resolve-name]
return MSApp.execUnsafeLocalFunction(function () {
return setInnerHTMLImpl(node, html);
});
};
}
export default setInnerHTML;

View File

@ -18,7 +18,7 @@ import {TEXT_NODE} from './HTMLNodeType';
* @param {string} text
* @internal
*/
const setTextContent = function (node: Element, text: string): void {
function setTextContent(node: Element, text: string): void {
if (text) {
const firstChild = node.firstChild;
@ -32,6 +32,6 @@ const setTextContent = function (node: Element, text: string): void {
}
}
node.textContent = text;
};
}
export default setTextContent;

View File

@ -7,12 +7,6 @@
* @flow
*/
type ValidateDOMNesting = (?string, ?string, AncestorInfoDev) => void;
let validateDOMNesting: ValidateDOMNesting = (() => {}: any);
type UpdatedAncestorInfoDev = (?AncestorInfoDev, string) => AncestorInfoDev;
let updatedAncestorInfoDev: UpdatedAncestorInfoDev = (() => {}: any);
type Info = {tag: string};
export type AncestorInfoDev = {
current: ?Info,
@ -30,156 +24,159 @@ export type AncestorInfoDev = {
containerTagInScope: ?Info,
};
if (__DEV__) {
// This validation code was written based on the HTML5 parsing spec:
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
//
// Note: this does not catch all invalid nesting, nor does it try to (as it's
// not clear what practical benefit doing so provides); instead, we warn only
// for cases where the parser will give a parse tree differing from what React
// intended. For example, <b><div></div></b> is invalid but we don't warn
// because it still parses correctly; we do warn for other cases like nested
// <p> tags where the beginning of the second element implicitly closes the
// first, causing a confusing mess.
// This validation code was written based on the HTML5 parsing spec:
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
//
// Note: this does not catch all invalid nesting, nor does it try to (as it's
// not clear what practical benefit doing so provides); instead, we warn only
// for cases where the parser will give a parse tree differing from what React
// intended. For example, <b><div></div></b> is invalid but we don't warn
// because it still parses correctly; we do warn for other cases like nested
// <p> tags where the beginning of the second element implicitly closes the
// first, causing a confusing mess.
// https://html.spec.whatwg.org/multipage/syntax.html#special
const specialTags = [
'address',
'applet',
'area',
'article',
'aside',
'base',
'basefont',
'bgsound',
'blockquote',
'body',
'br',
'button',
'caption',
'center',
'col',
'colgroup',
'dd',
'details',
'dir',
'div',
'dl',
'dt',
'embed',
'fieldset',
'figcaption',
'figure',
'footer',
'form',
'frame',
'frameset',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'head',
'header',
'hgroup',
'hr',
'html',
'iframe',
'img',
'input',
'isindex',
'li',
'link',
'listing',
'main',
'marquee',
'menu',
'menuitem',
'meta',
'nav',
'noembed',
'noframes',
'noscript',
'object',
'ol',
'p',
'param',
'plaintext',
'pre',
'script',
'section',
'select',
'source',
'style',
'summary',
'table',
'tbody',
'td',
'template',
'textarea',
'tfoot',
'th',
'thead',
'title',
'tr',
'track',
'ul',
'wbr',
'xmp',
];
// https://html.spec.whatwg.org/multipage/syntax.html#special
const specialTags = [
'address',
'applet',
'area',
'article',
'aside',
'base',
'basefont',
'bgsound',
'blockquote',
'body',
'br',
'button',
'caption',
'center',
'col',
'colgroup',
'dd',
'details',
'dir',
'div',
'dl',
'dt',
'embed',
'fieldset',
'figcaption',
'figure',
'footer',
'form',
'frame',
'frameset',
'h1',
'h2',
'h3',
'h4',
'h5',
'h6',
'head',
'header',
'hgroup',
'hr',
'html',
'iframe',
'img',
'input',
'isindex',
'li',
'link',
'listing',
'main',
'marquee',
'menu',
'menuitem',
'meta',
'nav',
'noembed',
'noframes',
'noscript',
'object',
'ol',
'p',
'param',
'plaintext',
'pre',
'script',
'section',
'select',
'source',
'style',
'summary',
'table',
'tbody',
'td',
'template',
'textarea',
'tfoot',
'th',
'thead',
'title',
'tr',
'track',
'ul',
'wbr',
'xmp',
];
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
const inScopeTags = [
'applet',
'caption',
'html',
'table',
'td',
'th',
'marquee',
'object',
'template',
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-scope
const inScopeTags = [
'applet',
'caption',
'html',
'table',
'td',
'th',
'marquee',
'object',
'template',
// https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
// TODO: Distinguish by namespace here -- for <title>, including it here
// errs on the side of fewer warnings
'foreignObject',
'desc',
'title',
];
// https://html.spec.whatwg.org/multipage/syntax.html#html-integration-point
// TODO: Distinguish by namespace here -- for <title>, including it here
// errs on the side of fewer warnings
'foreignObject',
'desc',
'title',
];
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
const buttonScopeTags = inScopeTags.concat(['button']);
// https://html.spec.whatwg.org/multipage/syntax.html#has-an-element-in-button-scope
const buttonScopeTags = __DEV__ ? inScopeTags.concat(['button']) : [];
// https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
const impliedEndTags = [
'dd',
'dt',
'li',
'option',
'optgroup',
'p',
'rp',
'rt',
];
// https://html.spec.whatwg.org/multipage/syntax.html#generate-implied-end-tags
const impliedEndTags = [
'dd',
'dt',
'li',
'option',
'optgroup',
'p',
'rp',
'rt',
];
const emptyAncestorInfoDev: AncestorInfoDev = {
current: null,
const emptyAncestorInfoDev: AncestorInfoDev = {
current: null,
formTag: null,
aTagInScope: null,
buttonTagInScope: null,
nobrTagInScope: null,
pTagInButtonScope: null,
formTag: null,
aTagInScope: null,
buttonTagInScope: null,
nobrTagInScope: null,
pTagInButtonScope: null,
listItemTagAutoclosing: null,
dlItemTagAutoclosing: null,
listItemTagAutoclosing: null,
dlItemTagAutoclosing: null,
containerTagInScope: null,
};
containerTagInScope: null,
};
updatedAncestorInfoDev = function (oldInfo: ?AncestorInfoDev, tag: string) {
function updatedAncestorInfoDev(
oldInfo: ?AncestorInfoDev,
tag: string,
): AncestorInfoDev {
if (__DEV__) {
const ancestorInfo = {...(oldInfo || emptyAncestorInfoDev)};
const info = {tag};
@ -234,213 +231,214 @@ if (__DEV__) {
}
return ancestorInfo;
};
} else {
return (null: any);
}
}
/**
* Returns whether
*/
const isTagValidWithParent = function (
tag: string,
parentTag: ?string,
): boolean {
// First, let's check if we're in an unusual parsing mode...
switch (parentTag) {
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
case 'select':
return tag === 'option' || tag === 'optgroup' || tag === '#text';
case 'optgroup':
return tag === 'option' || tag === '#text';
// Strictly speaking, seeing an <option> doesn't mean we're in a <select>
// but
case 'option':
return tag === '#text';
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption
// No special behavior since these rules fall back to "in body" mode for
// all except special table nodes which cause bad parsing behavior anyway.
/**
* Returns whether
*/
function isTagValidWithParent(tag: string, parentTag: ?string): boolean {
// First, let's check if we're in an unusual parsing mode...
switch (parentTag) {
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inselect
case 'select':
return tag === 'option' || tag === 'optgroup' || tag === '#text';
case 'optgroup':
return tag === 'option' || tag === '#text';
// Strictly speaking, seeing an <option> doesn't mean we're in a <select>
// but
case 'option':
return tag === '#text';
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intd
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incaption
// No special behavior since these rules fall back to "in body" mode for
// all except special table nodes which cause bad parsing behavior anyway.
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intr
case 'tr':
return (
tag === 'th' ||
tag === 'td' ||
tag === 'style' ||
tag === 'script' ||
tag === 'template'
);
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intbody
case 'tbody':
case 'thead':
case 'tfoot':
return (
tag === 'tr' ||
tag === 'style' ||
tag === 'script' ||
tag === 'template'
);
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incolgroup
case 'colgroup':
return tag === 'col' || tag === 'template';
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intable
case 'table':
return (
tag === 'caption' ||
tag === 'colgroup' ||
tag === 'tbody' ||
tag === 'tfoot' ||
tag === 'thead' ||
tag === 'style' ||
tag === 'script' ||
tag === 'template'
);
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inhead
case 'head':
return (
tag === 'base' ||
tag === 'basefont' ||
tag === 'bgsound' ||
tag === 'link' ||
tag === 'meta' ||
tag === 'title' ||
tag === 'noscript' ||
tag === 'noframes' ||
tag === 'style' ||
tag === 'script' ||
tag === 'template'
);
// https://html.spec.whatwg.org/multipage/semantics.html#the-html-element
case 'html':
return tag === 'head' || tag === 'body' || tag === 'frameset';
case 'frameset':
return tag === 'frame';
case '#document':
return tag === 'html';
}
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intr
case 'tr':
return (
tag === 'th' ||
tag === 'td' ||
tag === 'style' ||
tag === 'script' ||
tag === 'template'
);
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intbody
case 'tbody':
case 'thead':
case 'tfoot':
return (
tag === 'tr' ||
tag === 'style' ||
tag === 'script' ||
tag === 'template'
);
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-incolgroup
case 'colgroup':
return tag === 'col' || tag === 'template';
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-intable
case 'table':
return (
tag === 'caption' ||
tag === 'colgroup' ||
tag === 'tbody' ||
tag === 'tfoot' ||
tag === 'thead' ||
tag === 'style' ||
tag === 'script' ||
tag === 'template'
);
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inhead
case 'head':
return (
tag === 'base' ||
tag === 'basefont' ||
tag === 'bgsound' ||
tag === 'link' ||
tag === 'meta' ||
tag === 'title' ||
tag === 'noscript' ||
tag === 'noframes' ||
tag === 'style' ||
tag === 'script' ||
tag === 'template'
);
// https://html.spec.whatwg.org/multipage/semantics.html#the-html-element
case 'html':
return tag === 'head' || tag === 'body' || tag === 'frameset';
case 'frameset':
return tag === 'frame';
case '#document':
return tag === 'html';
}
// Probably in the "in body" parsing mode, so we outlaw only tag combos
// where the parsing rules cause implicit opens or closes to be added.
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
switch (tag) {
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
return (
parentTag !== 'h1' &&
parentTag !== 'h2' &&
parentTag !== 'h3' &&
parentTag !== 'h4' &&
parentTag !== 'h5' &&
parentTag !== 'h6'
);
// Probably in the "in body" parsing mode, so we outlaw only tag combos
// where the parsing rules cause implicit opens or closes to be added.
// https://html.spec.whatwg.org/multipage/syntax.html#parsing-main-inbody
switch (tag) {
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
return (
parentTag !== 'h1' &&
parentTag !== 'h2' &&
parentTag !== 'h3' &&
parentTag !== 'h4' &&
parentTag !== 'h5' &&
parentTag !== 'h6'
);
case 'rp':
case 'rt':
return impliedEndTags.indexOf(parentTag) === -1;
case 'rp':
case 'rt':
return impliedEndTags.indexOf(parentTag) === -1;
case 'body':
case 'caption':
case 'col':
case 'colgroup':
case 'frameset':
case 'frame':
case 'head':
case 'html':
case 'tbody':
case 'td':
case 'tfoot':
case 'th':
case 'thead':
case 'tr':
// These tags are only valid with a few parents that have special child
// parsing rules -- if we're down here, then none of those matched and
// so we allow it only if we don't know what the parent is, as all other
// cases are invalid.
return parentTag == null;
}
case 'body':
case 'caption':
case 'col':
case 'colgroup':
case 'frameset':
case 'frame':
case 'head':
case 'html':
case 'tbody':
case 'td':
case 'tfoot':
case 'th':
case 'thead':
case 'tr':
// These tags are only valid with a few parents that have special child
// parsing rules -- if we're down here, then none of those matched and
// so we allow it only if we don't know what the parent is, as all other
// cases are invalid.
return parentTag == null;
}
return true;
};
return true;
}
/**
* Returns whether
*/
const findInvalidAncestorForTag = function (
tag: string,
ancestorInfo: AncestorInfoDev,
): ?Info {
switch (tag) {
case 'address':
case 'article':
case 'aside':
case 'blockquote':
case 'center':
case 'details':
case 'dialog':
case 'dir':
case 'div':
case 'dl':
case 'fieldset':
case 'figcaption':
case 'figure':
case 'footer':
case 'header':
case 'hgroup':
case 'main':
case 'menu':
case 'nav':
case 'ol':
case 'p':
case 'section':
case 'summary':
case 'ul':
case 'pre':
case 'listing':
case 'table':
case 'hr':
case 'xmp':
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
return ancestorInfo.pTagInButtonScope;
/**
* Returns whether
*/
function findInvalidAncestorForTag(
tag: string,
ancestorInfo: AncestorInfoDev,
): ?Info {
switch (tag) {
case 'address':
case 'article':
case 'aside':
case 'blockquote':
case 'center':
case 'details':
case 'dialog':
case 'dir':
case 'div':
case 'dl':
case 'fieldset':
case 'figcaption':
case 'figure':
case 'footer':
case 'header':
case 'hgroup':
case 'main':
case 'menu':
case 'nav':
case 'ol':
case 'p':
case 'section':
case 'summary':
case 'ul':
case 'pre':
case 'listing':
case 'table':
case 'hr':
case 'xmp':
case 'h1':
case 'h2':
case 'h3':
case 'h4':
case 'h5':
case 'h6':
return ancestorInfo.pTagInButtonScope;
case 'form':
return ancestorInfo.formTag || ancestorInfo.pTagInButtonScope;
case 'form':
return ancestorInfo.formTag || ancestorInfo.pTagInButtonScope;
case 'li':
return ancestorInfo.listItemTagAutoclosing;
case 'li':
return ancestorInfo.listItemTagAutoclosing;
case 'dd':
case 'dt':
return ancestorInfo.dlItemTagAutoclosing;
case 'dd':
case 'dt':
return ancestorInfo.dlItemTagAutoclosing;
case 'button':
return ancestorInfo.buttonTagInScope;
case 'button':
return ancestorInfo.buttonTagInScope;
case 'a':
// Spec says something about storing a list of markers, but it sounds
// equivalent to this check.
return ancestorInfo.aTagInScope;
case 'a':
// Spec says something about storing a list of markers, but it sounds
// equivalent to this check.
return ancestorInfo.aTagInScope;
case 'nobr':
return ancestorInfo.nobrTagInScope;
}
case 'nobr':
return ancestorInfo.nobrTagInScope;
}
return null;
};
return null;
}
const didWarn: {[string]: boolean} = {};
const didWarn: {[string]: boolean} = {};
validateDOMNesting = function (
childTag: ?string,
childText: ?string,
ancestorInfo: AncestorInfoDev,
) {
function validateDOMNesting(
childTag: ?string,
childText: ?string,
ancestorInfo: AncestorInfoDev,
): void {
if (__DEV__) {
ancestorInfo = ancestorInfo || emptyAncestorInfoDev;
const parentInfo = ancestorInfo.current;
const parentTag = parentInfo && parentInfo.tag;
@ -517,7 +515,7 @@ if (__DEV__) {
ancestorTag,
);
}
};
}
}
export {updatedAncestorInfoDev, validateDOMNesting};

View File

@ -16,16 +16,18 @@ import isCustomComponent from './isCustomComponent';
import possibleStandardNames from './possibleStandardNames';
import hasOwnProperty from 'shared/hasOwnProperty';
let validateProperty = () => {};
const warnedProperties = {};
const EVENT_NAME_REGEX = /^on./;
const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
const rARIA = __DEV__
? new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$')
: null;
const rARIACamel = __DEV__
? new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$')
: null;
if (__DEV__) {
const warnedProperties = {};
const EVENT_NAME_REGEX = /^on./;
const INVALID_EVENT_NAME_REGEX = /^on[^A-Z]/;
const rARIA = new RegExp('^(aria)-[' + ATTRIBUTE_NAME_CHAR + ']*$');
const rARIACamel = new RegExp('^(aria)[A-Z][' + ATTRIBUTE_NAME_CHAR + ']*$');
validateProperty = function (tagName, name, value, eventRegistry) {
function validateProperty(tagName, name, value, eventRegistry) {
if (__DEV__) {
if (hasOwnProperty.call(warnedProperties, name) && warnedProperties[name]) {
return true;
}
@ -234,10 +236,10 @@ if (__DEV__) {
}
return true;
};
}
}
const warnUnknownProperties = function (type, props, eventRegistry) {
function warnUnknownProperties(type, props, eventRegistry) {
if (__DEV__) {
const unknownProps = [];
for (const key in props) {
@ -268,7 +270,7 @@ const warnUnknownProperties = function (type, props, eventRegistry) {
);
}
}
};
}
export function validateProperties(type, props, eventRegistry) {
if (isCustomComponent(type, props)) {

View File

@ -5,29 +5,27 @@
* LICENSE file in the root directory of this source tree.
*/
let warnValidStyle = () => {};
// 'msTransform' is correct, but the other prefixes should be capitalized
const badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
const msPattern = /^-ms-/;
const hyphenPattern = /-(.)/g;
if (__DEV__) {
// 'msTransform' is correct, but the other prefixes should be capitalized
const badVendoredStyleNamePattern = /^(?:webkit|moz|o)[A-Z]/;
const msPattern = /^-ms-/;
const hyphenPattern = /-(.)/g;
// style values shouldn't contain a semicolon
const badStyleValueWithSemicolonPattern = /;\s*$/;
// style values shouldn't contain a semicolon
const badStyleValueWithSemicolonPattern = /;\s*$/;
const warnedStyleNames = {};
const warnedStyleValues = {};
let warnedForNaNValue = false;
let warnedForInfinityValue = false;
const warnedStyleNames = {};
const warnedStyleValues = {};
let warnedForNaNValue = false;
let warnedForInfinityValue = false;
function camelize(string) {
return string.replace(hyphenPattern, function (_, character) {
return character.toUpperCase();
});
}
const camelize = function (string) {
return string.replace(hyphenPattern, function (_, character) {
return character.toUpperCase();
});
};
const warnHyphenatedStyleName = function (name) {
function warnHyphenatedStyleName(name) {
if (__DEV__) {
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
return;
}
@ -41,9 +39,11 @@ if (__DEV__) {
// is converted to lowercase `ms`.
camelize(name.replace(msPattern, 'ms-')),
);
};
}
}
const warnBadVendoredStyleName = function (name) {
function warnBadVendoredStyleName(name) {
if (__DEV__) {
if (warnedStyleNames.hasOwnProperty(name) && warnedStyleNames[name]) {
return;
}
@ -54,9 +54,11 @@ if (__DEV__) {
name,
name.charAt(0).toUpperCase() + name.slice(1),
);
};
}
}
const warnStyleValueWithSemicolon = function (name, value) {
function warnStyleValueWithSemicolon(name, value) {
if (__DEV__) {
if (warnedStyleValues.hasOwnProperty(value) && warnedStyleValues[value]) {
return;
}
@ -68,9 +70,11 @@ if (__DEV__) {
name,
value.replace(badStyleValueWithSemicolonPattern, ''),
);
};
}
}
const warnStyleValueIsNaN = function (name, value) {
function warnStyleValueIsNaN(name, value) {
if (__DEV__) {
if (warnedForNaNValue) {
return;
}
@ -80,9 +84,11 @@ if (__DEV__) {
'`NaN` is an invalid value for the `%s` css style property.',
name,
);
};
}
}
const warnStyleValueIsInfinity = function (name, value) {
function warnStyleValueIsInfinity(name, value) {
if (__DEV__) {
if (warnedForInfinityValue) {
return;
}
@ -92,9 +98,11 @@ if (__DEV__) {
'`Infinity` is an invalid value for the `%s` css style property.',
name,
);
};
}
}
warnValidStyle = function (name, value) {
function warnValidStyle(name, value) {
if (__DEV__) {
if (name.indexOf('-') > -1) {
warnHyphenatedStyleName(name);
} else if (badVendoredStyleNamePattern.test(name)) {
@ -110,7 +118,7 @@ if (__DEV__) {
warnStyleValueIsInfinity(name, value);
}
}
};
}
}
export default warnValidStyle;

View File

@ -392,7 +392,7 @@ function executeDispatchesInOrder(event) {
* @param {?object} event Synthetic event to be dispatched.
* @private
*/
const executeDispatchesAndRelease = function (event /* ReactSyntheticEvent */) {
function executeDispatchesAndRelease(event /* ReactSyntheticEvent */) {
if (event) {
executeDispatchesInOrder(event);
@ -400,7 +400,7 @@ const executeDispatchesAndRelease = function (event /* ReactSyntheticEvent */) {
event.constructor.release(event);
}
}
};
}
function isInteractive(tag) {
return (

View File

@ -44,13 +44,13 @@ const EMPTY_NATIVE_EVENT = (({}: any): AnyNativeEvent);
* @return {Array<Touch>} Subsequence of touch objects.
*/
// $FlowFixMe[missing-local-annot]
const touchSubsequence = function (touches, indices) {
function touchSubsequence(touches, indices) {
const ret = [];
for (let i = 0; i < indices.length; i++) {
ret.push(touches[indices[i]]);
}
return ret;
};
}
/**
* TODO: Pool all of this.
@ -63,7 +63,7 @@ const touchSubsequence = function (touches, indices) {
* @param {Array<number>} indices Indices to remove from `touches`.
* @return {Array<Touch>} Subsequence of removed touch objects.
*/
const removeTouchesAtIndices = function (
function removeTouchesAtIndices(
touches: Array<Object>,
indices: Array<number>,
): Array<Object> {
@ -85,7 +85,7 @@ const removeTouchesAtIndices = function (
}
temp.length = fillAt;
return rippedOut;
};
}
/**
* Internal version of `receiveEvent` in terms of normalized (non-tag)

View File

@ -28,49 +28,39 @@ if (__DEV__) {
Object.freeze(emptyObject);
}
let createHierarchy;
let getHostNode;
let getHostProps;
let lastNonHostInstance;
let getInspectorDataForInstance: (
closestInstance: Fiber | null,
) => InspectorData;
let getOwnerHierarchy;
let traverseOwnerTreeUp: (hierarchy: Array<$FlowFixMe>, instance: any) => void;
// $FlowFixMe[missing-local-annot]
function createHierarchy(fiberHierarchy) {
return fiberHierarchy.map(fiber => ({
name: getComponentNameFromType(fiber.type),
getInspectorData: findNodeHandle => {
return {
props: getHostProps(fiber),
source: fiber._debugSource,
measure: callback => {
// If this is Fabric, we'll find a shadow node and use that to measure.
const hostFiber = findCurrentHostFiber(fiber);
const node =
hostFiber != null &&
hostFiber.stateNode !== null &&
hostFiber.stateNode.node;
if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
// $FlowFixMe[missing-local-annot]
createHierarchy = function (fiberHierarchy) {
return fiberHierarchy.map(fiber => ({
name: getComponentNameFromType(fiber.type),
getInspectorData: findNodeHandle => {
return {
props: getHostProps(fiber),
source: fiber._debugSource,
measure: callback => {
// If this is Fabric, we'll find a shadow node and use that to measure.
const hostFiber = findCurrentHostFiber(fiber);
const node =
hostFiber != null &&
hostFiber.stateNode !== null &&
hostFiber.stateNode.node;
if (node) {
nativeFabricUIManager.measure(node, callback);
} else {
return UIManager.measure(
getHostNode(fiber, findNodeHandle),
callback,
);
}
},
};
},
}));
}
if (node) {
nativeFabricUIManager.measure(node, callback);
} else {
return UIManager.measure(
getHostNode(fiber, findNodeHandle),
callback,
);
}
},
};
},
}));
};
// $FlowFixMe[missing-local-annot]
getHostNode = function (fiber: Fiber | null, findNodeHandle) {
// $FlowFixMe[missing-local-annot]
function getHostNode(fiber: Fiber | null, findNodeHandle) {
if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
let hostNode;
// look for children first for the hostNode
// as composite fibers do not have a hostNode
@ -84,20 +74,22 @@ if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
fiber = fiber.child;
}
return null;
};
}
}
// $FlowFixMe[missing-local-annot]
getHostProps = function (fiber) {
const host = findCurrentHostFiber(fiber);
if (host) {
return host.memoizedProps || emptyObject;
}
return emptyObject;
};
// $FlowFixMe[missing-local-annot]
function getHostProps(fiber) {
const host = findCurrentHostFiber(fiber);
if (host) {
return host.memoizedProps || emptyObject;
}
return emptyObject;
}
getInspectorDataForInstance = function (
closestInstance: Fiber | null,
): InspectorData {
function getInspectorDataForInstance(
closestInstance: Fiber | null,
): InspectorData {
if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
// Handle case where user clicks outside of ReactNative
if (!closestInstance) {
return {
@ -123,46 +115,44 @@ if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
selectedIndex,
source,
};
};
} else {
return (null: any);
}
}
getOwnerHierarchy = function (instance: any) {
const hierarchy: Array<$FlowFixMe> = [];
traverseOwnerTreeUp(hierarchy, instance);
return hierarchy;
};
function getOwnerHierarchy(instance: any) {
const hierarchy: Array<$FlowFixMe> = [];
traverseOwnerTreeUp(hierarchy, instance);
return hierarchy;
}
// $FlowFixMe[missing-local-annot]
lastNonHostInstance = function (hierarchy) {
for (let i = hierarchy.length - 1; i > 1; i--) {
const instance = hierarchy[i];
// $FlowFixMe[missing-local-annot]
function lastNonHostInstance(hierarchy) {
for (let i = hierarchy.length - 1; i > 1; i--) {
const instance = hierarchy[i];
if (instance.tag !== HostComponent) {
return instance;
}
if (instance.tag !== HostComponent) {
return instance;
}
return hierarchy[0];
};
}
return hierarchy[0];
}
// $FlowFixMe[missing-local-annot]
traverseOwnerTreeUp = function (hierarchy, instance: any): void {
// $FlowFixMe[missing-local-annot]
function traverseOwnerTreeUp(
hierarchy: Array<$FlowFixMe>,
instance: any,
): void {
if (__DEV__ || enableGetInspectorDataForInstanceInProduction) {
if (instance) {
hierarchy.unshift(instance);
traverseOwnerTreeUp(hierarchy, instance._debugOwner);
}
};
}
}
let getInspectorDataForViewTag: (viewTag: number) => Object;
let getInspectorDataForViewAtPoint: (
findNodeHandle: (componentOrHandle: any) => ?number,
inspectedView: Object,
locationX: number,
locationY: number,
callback: (viewData: TouchedViewDataAtPoint) => mixed,
) => void;
if (__DEV__) {
getInspectorDataForViewTag = function (viewTag: number): Object {
function getInspectorDataForViewTag(viewTag: number): Object {
if (__DEV__) {
const closestInstance = getClosestInstanceFromNode(viewTag);
// Handle case where user clicks outside of ReactNative
@ -189,15 +179,21 @@ if (__DEV__) {
selectedIndex,
source,
};
};
} else {
throw new Error(
'getInspectorDataForViewTag() is not available in production',
);
}
}
getInspectorDataForViewAtPoint = function (
findNodeHandle: (componentOrHandle: any) => ?number,
inspectedView: Object,
locationX: number,
locationY: number,
callback: (viewData: TouchedViewDataAtPoint) => mixed,
): void {
function getInspectorDataForViewAtPoint(
findNodeHandle: (componentOrHandle: any) => ?number,
inspectedView: Object,
locationX: number,
locationY: number,
callback: (viewData: TouchedViewDataAtPoint) => mixed,
): void {
if (__DEV__) {
let closestInstance = null;
const fabricInstanceHandle =
@ -271,25 +267,11 @@ if (__DEV__) {
return;
}
};
} else {
getInspectorDataForViewTag = () => {
throw new Error(
'getInspectorDataForViewTag() is not available in production',
);
};
getInspectorDataForViewAtPoint = (
findNodeHandle: (componentOrHandle: any) => ?number,
inspectedView: Object,
locationX: number,
locationY: number,
callback: (viewData: TouchedViewDataAtPoint) => mixed,
): void => {
} else {
throw new Error(
'getInspectorDataForViewAtPoint() is not available in production.',
);
};
}
}
export {

View File

@ -15,7 +15,7 @@ type Options = {+unsafelyIgnoreFunctions?: boolean};
/*
* @returns {bool} true if different, false if equal
*/
const deepDiffer = function (
function deepDiffer(
one: any,
two: any,
maxDepthOrOptions: Options | number = -1,
@ -79,6 +79,6 @@ const deepDiffer = function (
}
}
return false;
};
}
module.exports = deepDiffer;

View File

@ -9,6 +9,6 @@
// TODO: move into react or fbjs
const deepFreezeAndThrowOnMutationInDev = function () {};
function deepFreezeAndThrowOnMutationInDev() {}
module.exports = deepFreezeAndThrowOnMutationInDev;

View File

@ -9,6 +9,6 @@
// TODO: Move flattenStyle into react
const flattenStyle = function () {};
function flattenStyle() {}
module.exports = flattenStyle;

View File

@ -25,7 +25,7 @@ let eventQueue: ?(Array<ReactSyntheticEvent> | ReactSyntheticEvent) = null;
* @param {?object} event Synthetic event to be dispatched.
* @private
*/
const executeDispatchesAndRelease = function (event: ReactSyntheticEvent) {
function executeDispatchesAndRelease(event: ReactSyntheticEvent) {
if (event) {
executeDispatchesInOrder(event);
@ -33,11 +33,11 @@ const executeDispatchesAndRelease = function (event: ReactSyntheticEvent) {
event.constructor.release(event);
}
}
};
}
// $FlowFixMe[missing-local-annot]
const executeDispatchesAndReleaseTopLevel = function (e) {
function executeDispatchesAndReleaseTopLevel(e) {
return executeDispatchesAndRelease(e);
};
}
export function runEventsInBatch(
events: Array<ReactSyntheticEvent> | ReactSyntheticEvent | null,

View File

@ -30,9 +30,8 @@ export function setComponentTree(
}
}
let validateEventDispatches;
if (__DEV__) {
validateEventDispatches = function (event) {
function validateEventDispatches(event) {
if (__DEV__) {
const dispatchListeners = event._dispatchListeners;
const dispatchInstances = event._dispatchInstances;
@ -53,7 +52,7 @@ if (__DEV__) {
if (instancesIsArr !== listenersIsArr || instancesLen !== listenersLen) {
console.error('EventPluginUtils: Invalid `event`.');
}
};
}
}
/**

View File

@ -44,7 +44,7 @@ let responderInst = null;
*/
let trackedTouchCount = 0;
const changeResponder = function (nextResponderInst, blockHostResponder) {
function changeResponder(nextResponderInst, blockHostResponder) {
const oldResponderInst = responderInst;
responderInst = nextResponderInst;
if (ResponderEventPlugin.GlobalResponderHandler !== null) {
@ -54,7 +54,7 @@ const changeResponder = function (nextResponderInst, blockHostResponder) {
blockHostResponder,
);
}
};
}
const eventTypes = {
/**

View File

@ -224,7 +224,7 @@ function FiberNode(
// is faster.
// 5) It should be easy to port this to a C struct and keep a C implementation
// compatible.
const createFiber = function (
function createFiber(
tag: WorkTag,
pendingProps: mixed,
key: null | string,
@ -232,7 +232,7 @@ const createFiber = function (
): Fiber {
// $FlowFixMe: the shapes are exact here but Flow doesn't like constructors
return new FiberNode(tag, pendingProps, key, mode);
};
}
function shouldConstruct(Component: Function) {
const prototype = Component.prototype;

View File

@ -85,11 +85,10 @@ let didWarnAboutUninitializedState;
let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
let didWarnAboutLegacyLifecyclesAndDerivedState;
let didWarnAboutUndefinedDerivedState;
let warnOnUndefinedDerivedState;
let warnOnInvalidCallback;
let didWarnAboutDirectlyAssigningPropsToState;
let didWarnAboutContextTypeAndContextTypes;
let didWarnAboutInvalidateContextType;
let didWarnOnInvalidCallback;
if (__DEV__) {
didWarnAboutStateAssignmentForComponent = new Set<string>();
@ -100,38 +99,7 @@ if (__DEV__) {
didWarnAboutUndefinedDerivedState = new Set<string>();
didWarnAboutContextTypeAndContextTypes = new Set<string>();
didWarnAboutInvalidateContextType = new Set<string>();
const didWarnOnInvalidCallback = new Set<string>();
warnOnInvalidCallback = function (callback: mixed, callerName: string) {
if (callback === null || typeof callback === 'function') {
return;
}
const key = callerName + '_' + (callback: any);
if (!didWarnOnInvalidCallback.has(key)) {
didWarnOnInvalidCallback.add(key);
console.error(
'%s(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callerName,
callback,
);
}
};
warnOnUndefinedDerivedState = function (type: any, partialState: any) {
if (partialState === undefined) {
const componentName = getComponentNameFromType(type) || 'Component';
if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
didWarnAboutUndefinedDerivedState.add(componentName);
console.error(
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
'You have returned undefined.',
componentName,
);
}
}
};
didWarnOnInvalidCallback = new Set<string>();
// This is so gross but it's at least non-critical and can be removed if
// it causes problems. This is meant to give a nicer error message for
@ -154,6 +122,40 @@ if (__DEV__) {
Object.freeze(fakeInternalInstance);
}
function warnOnInvalidCallback(callback: mixed, callerName: string) {
if (__DEV__) {
if (callback === null || typeof callback === 'function') {
return;
}
const key = callerName + '_' + (callback: any);
if (!didWarnOnInvalidCallback.has(key)) {
didWarnOnInvalidCallback.add(key);
console.error(
'%s(...): Expected the last optional `callback` argument to be a ' +
'function. Instead received: %s.',
callerName,
callback,
);
}
}
}
function warnOnUndefinedDerivedState(type: any, partialState: any) {
if (__DEV__) {
if (partialState === undefined) {
const componentName = getComponentNameFromType(type) || 'Component';
if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
didWarnAboutUndefinedDerivedState.add(componentName);
console.error(
'%s.getDerivedStateFromProps(): A valid state object (or null) must be returned. ' +
'You have returned undefined.',
componentName,
);
}
}
}
}
function applyDerivedStateFromProps(
workInProgress: Fiber,
ctor: any,

View File

@ -253,10 +253,7 @@ export function reportUncaughtErrorInDEV(error: mixed) {
}
}
const callComponentWillUnmountWithTimer = function (
current: Fiber,
instance: any,
) {
function callComponentWillUnmountWithTimer(current: Fiber, instance: any) {
instance.props = current.memoizedProps;
instance.state = current.memoizedState;
if (shouldProfile(current)) {
@ -269,7 +266,7 @@ const callComponentWillUnmountWithTimer = function (
} else {
instance.componentWillUnmount();
}
};
}
// Capture errors so they don't interrupt unmounting.
function safelyCallComponentWillUnmount(

View File

@ -24,11 +24,10 @@ let didWarnAboutUninitializedState;
let didWarnAboutGetSnapshotBeforeUpdateWithoutDidUpdate;
let didWarnAboutLegacyLifecyclesAndDerivedState;
let didWarnAboutUndefinedDerivedState;
let warnOnUndefinedDerivedState;
let warnOnInvalidCallback;
let didWarnAboutDirectlyAssigningPropsToState;
let didWarnAboutContextTypeAndContextTypes;
let didWarnAboutInvalidateContextType;
let didWarnOnInvalidCallback;
if (__DEV__) {
didWarnAboutUninitializedState = new Set<string>();
@ -38,10 +37,11 @@ if (__DEV__) {
didWarnAboutUndefinedDerivedState = new Set<string>();
didWarnAboutContextTypeAndContextTypes = new Set<mixed>();
didWarnAboutInvalidateContextType = new Set<mixed>();
didWarnOnInvalidCallback = new Set<string>();
}
const didWarnOnInvalidCallback = new Set<string>();
warnOnInvalidCallback = function (callback: mixed, callerName: string) {
function warnOnInvalidCallback(callback: mixed, callerName: string) {
if (__DEV__) {
if (callback === null || typeof callback === 'function') {
return;
}
@ -55,9 +55,11 @@ if (__DEV__) {
callback,
);
}
};
}
}
warnOnUndefinedDerivedState = function (type: any, partialState: any) {
function warnOnUndefinedDerivedState(type: any, partialState: any) {
if (__DEV__) {
if (partialState === undefined) {
const componentName = getComponentNameFromType(type) || 'Component';
if (!didWarnAboutUndefinedDerivedState.has(componentName)) {
@ -69,7 +71,7 @@ if (__DEV__) {
);
}
}
};
}
}
function warnNoop(

View File

@ -145,7 +145,7 @@ function warnIfStringRefCannotBeAutoConverted(config) {
* indicating filename, line number, and/or other information.
* @internal
*/
const ReactElement = function (type, key, ref, self, source, owner, props) {
function ReactElement(type, key, ref, self, source, owner, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
@ -199,7 +199,7 @@ const ReactElement = function (type, key, ref, self, source, owner, props) {
}
return element;
};
}
/**
* https://github.com/reactjs/rfcs/pull/107

View File

@ -145,7 +145,7 @@ function defineRefPropWarningGetter(props, displayName) {
* indicating filename, line number, and/or other information.
* @internal
*/
const ReactElement = function (type, key, ref, self, source, owner, props) {
function ReactElement(type, key, ref, self, source, owner, props) {
const element = {
// This tag allows us to uniquely identify this as a React Element
$$typeof: REACT_ELEMENT_TYPE,
@ -199,7 +199,7 @@ const ReactElement = function (type, key, ref, self, source, owner, props) {
}
return element;
};
}
/**
* https://github.com/reactjs/rfcs/pull/107

View File

@ -16,7 +16,7 @@ if (typeof ReactFbErrorUtils.invokeGuardedCallback !== 'function') {
);
}
const invokeGuardedCallbackImpl = function <A, B, C, D, E, F, Context>(
function invokeGuardedCallbackImpl<A, B, C, D, E, F, Context>(
name: string | null,
func: (a: A, b: B, c: C, d: D, e: E, f: F) => mixed,
context: Context,
@ -29,6 +29,6 @@ const invokeGuardedCallbackImpl = function <A, B, C, D, E, F, Context>(
) {
// This will call `this.onError(err)` if an error was caught.
ReactFbErrorUtils.invokeGuardedCallback.apply(this, arguments);
};
}
export default invokeGuardedCallbackImpl;