@samitouri / QOS-React / commits / 745baf2e06

Provide new jsx transform target for reactjs/rfcs#107 (#15141)

* adding jsx function * add more feature flag defaults * flip ReactElement order back

Ricky Vetter committed Apr 7, 2019 at 15:02 UTC 745baf2e061bdb7a07b511fa6fd8c1c5e8106313
13 files changed +659 -12
packages/react/src/React.js
+21 -1
@@ -22,6 +22,7 @@ import {
22 createFactory,
23 cloneElement,
24 isValidElement,
25 + jsx,
26 } from './ReactElement';
27 import {createContext} from './ReactContext';
28 import {lazy} from './ReactLazy';
@@ -43,10 +44,16 @@ import {
44 createElementWithValidation,
45 createFactoryWithValidation,
46 cloneElementWithValidation,
47 + jsxWithValidation,
48 + jsxWithValidationStatic,
49 + jsxWithValidationDynamic,
50 } from './ReactElementValidator';
51 import ReactSharedInternals from './ReactSharedInternals';
52 import {error, warn} from './withComponentStack';
49 -import {enableStableConcurrentModeAPIs} from 'shared/ReactFeatureFlags';
53 +import {
54 + enableStableConcurrentModeAPIs,
55 + enableJSXTransformAPI,
56 +} from 'shared/ReactFeatureFlags';
57
58 const React = {
59 Children: {
@@ -107,4 +114,17 @@ if (enableStableConcurrentModeAPIs) {
114 React.unstable_ConcurrentMode = undefined;
115 }
116
117 +if (enableJSXTransformAPI) {
118 + if (__DEV__) {
119 + React.jsxDEV = jsxWithValidation;
120 + React.jsx = jsxWithValidationDynamic;
121 + React.jsxs = jsxWithValidationStatic;
122 + } else {
123 + React.jsx = jsx;
124 + // we may want to special case jsxs internally to take advantage of static children.
125 + // for now we can ship identical prod functions
126 + React.jsxs = jsx;
127 + }
128 +}
129 +
130 export default React;
packages/react/src/ReactElement.js
+135 -2
@@ -95,8 +95,10 @@ function defineRefPropWarningGetter(props, displayName) {
95 * if something is a React Element.
96 *
97 * @param {*} type
98 + * @param {*} props
99 * @param {*} key
100 * @param {string|object} ref
101 + * @param {*} owner
102 * @param {*} self A *temporary* helper to detect places where `this` is
103 * different from the `owner` when React.createElement is called, so that we
104 * can warn. We want to get rid of owner and replace string `ref`s with arrow
@@ -104,8 +106,6 @@ function defineRefPropWarningGetter(props, displayName) {
106 * change in behavior.
107 * @param {*} source An annotation object (added by a transpiler or otherwise)
108 * indicating filename, line number, and/or other information.
107 - * @param {*} owner
108 - * @param {*} props
109 * @internal
110 */
111 const ReactElement = function(type, key, ref, self, source, owner, props) {
@@ -164,6 +164,139 @@ const ReactElement = function(type, key, ref, self, source, owner, props) {
164 return element;
165 };
166
167 +/**
168 + * https://github.com/reactjs/rfcs/pull/107
169 + * @param {*} type
170 + * @param {object} props
171 + * @param {string} key
172 + */
173 +export function jsx(type, config, maybeKey) {
174 + let propName;
175 +
176 + // Reserved names are extracted
177 + const props = {};
178 +
179 + let key = null;
180 + let ref = null;
181 +
182 + if (hasValidRef(config)) {
183 + ref = config.ref;
184 + }
185 +
186 + if (hasValidKey(config)) {
187 + key = '' + config.key;
188 + }
189 +
190 + // Remaining properties are added to a new props object
191 + for (propName in config) {
192 + if (
193 + hasOwnProperty.call(config, propName) &&
194 + !RESERVED_PROPS.hasOwnProperty(propName)
195 + ) {
196 + props[propName] = config[propName];
197 + }
198 + }
199 +
200 + // intentionally not checking if key was set above
201 + // this key is higher priority as it's static
202 + if (maybeKey !== undefined) {
203 + key = '' + maybeKey;
204 + }
205 +
206 + // Resolve default props
207 + if (type && type.defaultProps) {
208 + const defaultProps = type.defaultProps;
209 + for (propName in defaultProps) {
210 + if (props[propName] === undefined) {
211 + props[propName] = defaultProps[propName];
212 + }
213 + }
214 + }
215 +
216 + return ReactElement(
217 + type,
218 + key,
219 + ref,
220 + undefined,
221 + undefined,
222 + ReactCurrentOwner.current,
223 + props,
224 + );
225 +}
226 +
227 +/**
228 + * https://github.com/reactjs/rfcs/pull/107
229 + * @param {*} type
230 + * @param {object} props
231 + * @param {string} key
232 + */
233 +export function jsxDEV(type, config, maybeKey, source, self) {
234 + let propName;
235 +
236 + // Reserved names are extracted
237 + const props = {};
238 +
239 + let key = null;
240 + let ref = null;
241 +
242 + if (hasValidRef(config)) {
243 + ref = config.ref;
244 + }
245 +
246 + if (hasValidKey(config)) {
247 + key = '' + config.key;
248 + }
249 +
250 + // Remaining properties are added to a new props object
251 + for (propName in config) {
252 + if (
253 + hasOwnProperty.call(config, propName) &&
254 + !RESERVED_PROPS.hasOwnProperty(propName)
255 + ) {
256 + props[propName] = config[propName];
257 + }
258 + }
259 +
260 + // intentionally not checking if key was set above
261 + // this key is higher priority as it's static
262 + if (maybeKey !== undefined) {
263 + key = '' + maybeKey;
264 + }
265 +
266 + // Resolve default props
267 + if (type && type.defaultProps) {
268 + const defaultProps = type.defaultProps;
269 + for (propName in defaultProps) {
270 + if (props[propName] === undefined) {
271 + props[propName] = defaultProps[propName];
272 + }
273 + }
274 + }
275 +
276 + if (key || ref) {
277 + const displayName =
278 + typeof type === 'function'
279 + ? type.displayName || type.name || 'Unknown'
280 + : type;
281 + if (key) {
282 + defineKeyPropWarningGetter(props, displayName);
283 + }
284 + if (ref) {
285 + defineRefPropWarningGetter(props, displayName);
286 + }
287 + }
288 +
289 + return ReactElement(
290 + type,
291 + key,
292 + ref,
293 + self,
294 + source,
295 + ReactCurrentOwner.current,
296 + props,
297 + );
298 +}
299 +
300 /**
301 * Create and return a new ReactElement of the given type.
302 * See https://reactjs.org/docs/react-api.html#createelement
packages/react/src/ReactElementValidator.js
+127 -9
@@ -27,7 +27,12 @@ import warning from 'shared/warning';
27 import warningWithoutStack from 'shared/warningWithoutStack';
28
29 import ReactCurrentOwner from './ReactCurrentOwner';
30 -import {isValidElement, createElement, cloneElement} from './ReactElement';
30 +import {
31 + isValidElement,
32 + createElement,
33 + cloneElement,
34 + jsxDEV,
35 +} from './ReactElement';
36 import ReactDebugCurrentFrame, {
37 setCurrentlyValidatingElement,
38 } from './ReactDebugCurrentFrame';
@@ -48,13 +53,8 @@ function getDeclarationErrorAddendum() {
53 return '';
54 }
55
51 -function getSourceInfoErrorAddendum(elementProps) {
52 - if (
53 - elementProps !== null &&
54 - elementProps !== undefined &&
55 - elementProps.__source !== undefined
56 - ) {
57 - const source = elementProps.__source;
56 +function getSourceInfoErrorAddendum(source) {
57 + if (source !== undefined) {
58 const fileName = source.fileName.replace(/^.*[\\\/]/, '');
59 const lineNumber = source.lineNumber;
60 return '\n\nCheck your code at ' + fileName + ':' + lineNumber + '.';
@@ -62,6 +62,13 @@ function getSourceInfoErrorAddendum(elementProps) {
62 return '';
63 }
64
65 +function getSourceInfoErrorAddendumForProps(elementProps) {
66 + if (elementProps !== null && elementProps !== undefined) {
67 + return getSourceInfoErrorAddendum(elementProps.__source);
68 + }
69 + return '';
70 +}
71 +
72 /**
73 * Warn if there's no key explicitly set on dynamic arrays of children or
74 * object keys are not valid. This allows us to keep track of children between
@@ -259,6 +266,117 @@ function validateFragmentProps(fragment) {
266 setCurrentlyValidatingElement(null);
267 }
268
269 +export function jsxWithValidation(
270 + type,
271 + props,
272 + key,
273 + isStaticChildren,
274 + source,
275 + self,
276 +) {
277 + const validType = isValidElementType(type);
278 +
279 + // We warn in this case but don't throw. We expect the element creation to
280 + // succeed and there will likely be errors in render.
281 + if (!validType) {
282 + let info = '';
283 + if (
284 + type === undefined ||
285 + (typeof type === 'object' &&
286 + type !== null &&
287 + Object.keys(type).length === 0)
288 + ) {
289 + info +=
290 + ' You likely forgot to export your component from the file ' +
291 + "it's defined in, or you might have mixed up default and named imports.";
292 + }
293 +
294 + const sourceInfo = getSourceInfoErrorAddendum(source);
295 + if (sourceInfo) {
296 + info += sourceInfo;
297 + } else {
298 + info += getDeclarationErrorAddendum();
299 + }
300 +
301 + let typeString;
302 + if (type === null) {
303 + typeString = 'null';
304 + } else if (Array.isArray(type)) {
305 + typeString = 'array';
306 + } else if (type !== undefined && type.$$typeof === REACT_ELEMENT_TYPE) {
307 + typeString = `<${getComponentName(type.type) || 'Unknown'} />`;
308 + info =
309 + ' Did you accidentally export a JSX literal instead of a component?';
310 + } else {
311 + typeString = typeof type;
312 + }
313 +
314 + warning(
315 + false,
316 + 'React.jsx: type is invalid -- expected a string (for ' +
317 + 'built-in components) or a class/function (for composite ' +
318 + 'components) but got: %s.%s',
319 + typeString,
320 + info,
321 + );
322 + }
323 +
324 + const element = jsxDEV(type, props, key, source, self);
325 +
326 + // The result can be nullish if a mock or a custom function is used.
327 + // TODO: Drop this when these are no longer allowed as the type argument.
328 + if (element == null) {
329 + return element;
330 + }
331 +
332 + // Skip key warning if the type isn't valid since our key validation logic
333 + // doesn't expect a non-string/function type and can throw confusing errors.
334 + // We don't want exception behavior to differ between dev and prod.
335 + // (Rendering will throw with a helpful message and as soon as the type is
336 + // fixed, the key warnings will appear.)
337 + if (validType) {
338 + const children = props.children;
339 + if (children !== undefined) {
340 + if (isStaticChildren) {
341 + for (let i = 0; i < children.length; i++) {
342 + validateChildKeys(children[i], type);
343 + }
344 + } else {
345 + validateChildKeys(children, type);
346 + }
347 + }
348 + }
349 +
350 + if (props.key !== undefined) {
351 + warning(
352 + false,
353 + 'React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
354 + 'Explicitly pass a key after spreading props in your JSX call. ' +
355 + 'E.g. <ComponentName {...props} key={key} />',
356 + );
357 + }
358 +
359 + if (type === REACT_FRAGMENT_TYPE) {
360 + validateFragmentProps(element);
361 + } else {
362 + validatePropTypes(element);
363 + }
364 +
365 + return element;
366 +}
367 +
368 +// These two functions exist to still get child warnings in dev
369 +// even with the prod transform. This means that jsxDEV is purely
370 +// opt-in behavior for better messages but that we won't stop
371 +// giving you warnings if you use production apis.
372 +export function jsxWithValidationStatic(type, props, key) {
373 + return jsxWithValidation(type, props, key, true);
374 +}
375 +
376 +export function jsxWithValidationDynamic(type, props, key) {
377 + return jsxWithValidation(type, props, key, false);
378 +}
379 +
380 export function createElementWithValidation(type, props, children) {
381 const validType = isValidElementType(type);
382
@@ -277,7 +395,7 @@ export function createElementWithValidation(type, props, children) {
395 "it's defined in, or you might have mixed up default and named imports.";
396 }
397
280 - const sourceInfo = getSourceInfoErrorAddendum(props);
398 + const sourceInfo = getSourceInfoErrorAddendumForProps(props);
399 if (sourceInfo) {
400 info += sourceInfo;
401 } else {
packages/react/src/__tests__/ReactElementJSX-test.internal.js new
+364
@@ -0,0 +1,364 @@
1 +/**
2 + * Copyright (c) Facebook, Inc. and its 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 + * @emails react-core
8 + */
9 +
10 +'use strict';
11 +
12 +let React;
13 +let ReactDOM;
14 +let ReactFeatureFlags;
15 +let ReactTestUtils;
16 +
17 +// NOTE: We're explicitly not using JSX here. This is intended to test
18 +// a new React.jsx api which does not have a JSX transformer yet.
19 +// A lot of these tests are pulled from ReactElement-test because
20 +// this api is meant to be backwards compatible.
21 +describe('ReactElement.jsx', () => {
22 + let originalSymbol;
23 +
24 + beforeEach(() => {
25 + jest.resetModules();
26 +
27 + // Delete the native Symbol if we have one to ensure we test the
28 + // unpolyfilled environment.
29 + originalSymbol = global.Symbol;
30 + global.Symbol = undefined;
31 +
32 + ReactFeatureFlags = require('shared/ReactFeatureFlags');
33 + ReactFeatureFlags.enableJSXTransformAPI = true;
34 +
35 + React = require('react');
36 + ReactDOM = require('react-dom');
37 + ReactTestUtils = require('react-dom/test-utils');
38 + });
39 +
40 + afterEach(() => {
41 + global.Symbol = originalSymbol;
42 + });
43 +
44 + it('allows static methods to be called using the type property', () => {
45 + class StaticMethodComponentClass extends React.Component {
46 + render() {
47 + return React.jsx('div', {});
48 + }
49 + }
50 + StaticMethodComponentClass.someStaticMethod = () => 'someReturnValue';
51 +
52 + const element = React.jsx(StaticMethodComponentClass, {});
53 + expect(element.type.someStaticMethod()).toBe('someReturnValue');
54 + });
55 +
56 + it('identifies valid elements', () => {
57 + class Component extends React.Component {
58 + render() {
59 + return React.jsx('div', {});
60 + }
61 + }
62 +
63 + expect(React.isValidElement(React.jsx('div', {}))).toEqual(true);
64 + expect(React.isValidElement(React.jsx(Component, {}))).toEqual(true);
65 +
66 + expect(React.isValidElement(null)).toEqual(false);
67 + expect(React.isValidElement(true)).toEqual(false);
68 + expect(React.isValidElement({})).toEqual(false);
69 + expect(React.isValidElement('string')).toEqual(false);
70 + expect(React.isValidElement(React.createFactory('div'))).toEqual(false);
71 + expect(React.isValidElement(Component)).toEqual(false);
72 + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);
73 +
74 + const jsonElement = JSON.stringify(React.jsx('div', {}));
75 + expect(React.isValidElement(JSON.parse(jsonElement))).toBe(true);
76 + });
77 +
78 + it('is indistinguishable from a plain object', () => {
79 + const element = React.jsx('div', {className: 'foo'});
80 + const object = {};
81 + expect(element.constructor).toBe(object.constructor);
82 + });
83 +
84 + it('should use default prop value when removing a prop', () => {
85 + class Component extends React.Component {
86 + render() {
87 + return React.jsx('span', {});
88 + }
89 + }
90 + Component.defaultProps = {fruit: 'persimmon'};
91 +
92 + const container = document.createElement('div');
93 + const instance = ReactDOM.render(
94 + React.jsx(Component, {fruit: 'mango'}),
95 + container,
96 + );
97 + expect(instance.props.fruit).toBe('mango');
98 +
99 + ReactDOM.render(React.jsx(Component, {}), container);
100 + expect(instance.props.fruit).toBe('persimmon');
101 + });
102 +
103 + it('should normalize props with default values', () => {
104 + class Component extends React.Component {
105 + render() {
106 + return React.jsx('span', {children: this.props.prop});
107 + }
108 + }
109 + Component.defaultProps = {prop: 'testKey'};
110 +
111 + const instance = ReactTestUtils.renderIntoDocument(
112 + React.jsx(Component, {}),
113 + );
114 + expect(instance.props.prop).toBe('testKey');
115 +
116 + const inst2 = ReactTestUtils.renderIntoDocument(
117 + React.jsx(Component, {prop: null}),
118 + );
119 + expect(inst2.props.prop).toBe(null);
120 + });
121 +
122 + it('throws when changing a prop (in dev) after element creation', () => {
123 + class Outer extends React.Component {
124 + render() {
125 + const el = React.jsx('div', {className: 'moo'});
126 +
127 + if (__DEV__) {
128 + expect(function() {
129 + el.props.className = 'quack';
130 + }).toThrow();
131 + expect(el.props.className).toBe('moo');
132 + } else {
133 + el.props.className = 'quack';
134 + expect(el.props.className).toBe('quack');
135 + }
136 +
137 + return el;
138 + }
139 + }
140 + const outer = ReactTestUtils.renderIntoDocument(
141 + React.jsx(Outer, {color: 'orange'}),
142 + );
143 + if (__DEV__) {
144 + expect(ReactDOM.findDOMNode(outer).className).toBe('moo');
145 + } else {
146 + expect(ReactDOM.findDOMNode(outer).className).toBe('quack');
147 + }
148 + });
149 +
150 + it('throws when adding a prop (in dev) after element creation', () => {
151 + const container = document.createElement('div');
152 + class Outer extends React.Component {
153 + render() {
154 + const el = React.jsx('div', {children: this.props.sound});
155 +
156 + if (__DEV__) {
157 + expect(function() {
158 + el.props.className = 'quack';
159 + }).toThrow();
160 + expect(el.props.className).toBe(undefined);
161 + } else {
162 + el.props.className = 'quack';
163 + expect(el.props.className).toBe('quack');
164 + }
165 +
166 + return el;
167 + }
168 + }
169 + Outer.defaultProps = {sound: 'meow'};
170 + const outer = ReactDOM.render(React.jsx(Outer, {}), container);
171 + expect(ReactDOM.findDOMNode(outer).textContent).toBe('meow');
172 + if (__DEV__) {
173 + expect(ReactDOM.findDOMNode(outer).className).toBe('');
174 + } else {
175 + expect(ReactDOM.findDOMNode(outer).className).toBe('quack');
176 + }
177 + });
178 +
179 + it('does not warn for NaN props', () => {
180 + class Test extends React.Component {
181 + render() {
182 + return React.jsx('div', {});
183 + }
184 + }
185 + const test = ReactTestUtils.renderIntoDocument(
186 + React.jsx(Test, {value: +undefined}),
187 + );
188 + expect(test.props.value).toBeNaN();
189 + });
190 +
191 + it('should warn when `key` is being accessed on composite element', () => {
192 + const container = document.createElement('div');
193 + class Child extends React.Component {
194 + render() {
195 + return React.jsx('div', {children: this.props.key});
196 + }
197 + }
198 + class Parent extends React.Component {
199 + render() {
200 + return React.jsxs('div', {
201 + children: [
202 + React.jsx(Child, {}, '0'),
203 + React.jsx(Child, {}, '1'),
204 + React.jsx(Child, {}, '2'),
205 + ],
206 + });
207 + }
208 + }
209 + expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toWarnDev(
210 + 'Child: `key` is not a prop. Trying to access it will result ' +
211 + 'in `undefined` being returned. If you need to access the same ' +
212 + 'value within the child component, you should pass it as a different ' +
213 + 'prop. (https://fb.me/react-special-props)',
214 + {withoutStack: true},
215 + );
216 + });
217 +
218 + it('should warn when `key` is being accessed on a host element', () => {
219 + const element = React.jsxs('div', {}, '3');
220 + expect(() => void element.props.key).toWarnDev(
221 + 'div: `key` is not a prop. Trying to access it will result ' +
222 + 'in `undefined` being returned. If you need to access the same ' +
223 + 'value within the child component, you should pass it as a different ' +
224 + 'prop. (https://fb.me/react-special-props)',
225 + {withoutStack: true},
226 + );
227 + });
228 +
229 + it('should warn when `ref` is being accessed', () => {
230 + const container = document.createElement('div');
231 + class Child extends React.Component {
232 + render() {
233 + return React.jsx('div', {children: this.props.ref});
234 + }
235 + }
236 + class Parent extends React.Component {
237 + render() {
238 + return React.jsx('div', {
239 + children: React.jsx(Child, {ref: 'childElement'}),
240 + });
241 + }
242 + }
243 + expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toWarnDev(
244 + 'Child: `ref` is not a prop. Trying to access it will result ' +
245 + 'in `undefined` being returned. If you need to access the same ' +
246 + 'value within the child component, you should pass it as a different ' +
247 + 'prop. (https://fb.me/react-special-props)',
248 + {withoutStack: true},
249 + );
250 + });
251 +
252 + it('identifies elements, but not JSON, if Symbols are supported', () => {
253 + // Rudimentary polyfill
254 + // Once all jest engines support Symbols natively we can swap this to test
255 + // WITH native Symbols by default.
256 + const REACT_ELEMENT_TYPE = function() {}; // fake Symbol
257 + const OTHER_SYMBOL = function() {}; // another fake Symbol
258 + global.Symbol = function(name) {
259 + return OTHER_SYMBOL;
260 + };
261 + global.Symbol.for = function(key) {
262 + if (key === 'react.element') {
263 + return REACT_ELEMENT_TYPE;
264 + }
265 + return OTHER_SYMBOL;
266 + };
267 +
268 + jest.resetModules();
269 +
270 + ReactFeatureFlags = require('shared/ReactFeatureFlags');
271 + ReactFeatureFlags.enableJSXTransformAPI = true;
272 +
273 + React = require('react');
274 +
275 + class Component extends React.Component {
276 + render() {
277 + return React.jsx('div');
278 + }
279 + }
280 +
281 + expect(React.isValidElement(React.jsx('div', {}))).toEqual(true);
282 + expect(React.isValidElement(React.jsx(Component, {}))).toEqual(true);
283 +
284 + expect(React.isValidElement(null)).toEqual(false);
285 + expect(React.isValidElement(true)).toEqual(false);
286 + expect(React.isValidElement({})).toEqual(false);
287 + expect(React.isValidElement('string')).toEqual(false);
288 + expect(React.isValidElement(React.createFactory('div'))).toEqual(false);
289 + expect(React.isValidElement(Component)).toEqual(false);
290 + expect(React.isValidElement({type: 'div', props: {}})).toEqual(false);
291 +
292 + const jsonElement = JSON.stringify(React.jsx('div', {}));
293 + expect(React.isValidElement(JSON.parse(jsonElement))).toBe(false);
294 + });
295 +
296 + it('should warn when unkeyed children are passed to jsx', () => {
297 + const container = document.createElement('div');
298 + class Child extends React.Component {
299 + render() {
300 + return React.jsx('div', {});
301 + }
302 + }
303 + class Parent extends React.Component {
304 + render() {
305 + return React.jsx('div', {
306 + children: [
307 + React.jsx(Child, {}),
308 + React.jsx(Child, {}),
309 + React.jsx(Child, {}),
310 + ],
311 + });
312 + }
313 + }
314 + expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toWarnDev(
315 + 'Warning: Each child in a list should have a unique "key" prop.\n\n' +
316 + 'Check the render method of `Parent`. See https://fb.me/react-warning-keys for more information.\n' +
317 + ' in Child (created by Parent)\n' +
318 + ' in Parent',
319 + );
320 + });
321 +
322 + it('should warn when keys are passed as part of props', () => {
323 + const container = document.createElement('div');
324 + class Child extends React.Component {
325 + render() {
326 + return React.jsx('div', {});
327 + }
328 + }
329 + class Parent extends React.Component {
330 + render() {
331 + return React.jsx('div', {
332 + children: [React.jsx(Child, {key: '0'})],
333 + });
334 + }
335 + }
336 + expect(() => ReactDOM.render(React.jsx(Parent, {}), container)).toWarnDev(
337 + 'Warning: React.jsx: Spreading a key to JSX is a deprecated pattern. ' +
338 + 'Explicitly pass a key after spreading props in your JSX call. ' +
339 + 'E.g. <ComponentName {...props} key={key} />',
340 + );
341 + });
342 +
343 + it('should not warn when unkeyed children are passed to jsxs', () => {
344 + const container = document.createElement('div');
345 + class Child extends React.Component {
346 + render() {
347 + return React.jsx('div', {});
348 + }
349 + }
350 + class Parent extends React.Component {
351 + render() {
352 + return React.jsxs('div', {
353 + children: [
354 + React.jsx(Child, {}),
355 + React.jsx(Child, {}),
356 + React.jsx(Child, {}),
357 + ],
358 + });
359 + }
360 + }
361 + // TODO: an explicit expect for no warning?
362 + ReactDOM.render(React.jsx(Parent, {}), container);
363 + });
364 +});
packages/shared/ReactFeatureFlags.js
+3
@@ -68,3 +68,6 @@ export const enableEventAPI = false;
68 // Enables rewritten version of ReactFiberScheduler. Added in case we need to
69 // quickly revert it.
70 export const enableNewScheduler = false;
71 +
72 +// New API for JSX transforms to target - https://github.com/reactjs/rfcs/pull/107
73 +export const enableJSXTransformAPI = false;
packages/shared/forks/ReactFeatureFlags.native-fb.js
+1
@@ -32,6 +32,7 @@ export const warnAboutDeprecatedLifecycles = true;
32 export const warnAboutDeprecatedSetNativeProps = true;
33 export const enableEventAPI = false;
34 export const enableNewScheduler = false;
35 +export const enableJSXTransformAPI = false;
36
37 // Only used in www builds.
38 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.native-oss.js
+1
@@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false;
29 export const warnAboutDeprecatedSetNativeProps = false;
30 export const enableEventAPI = false;
31 export const enableNewScheduler = false;
32 +export const enableJSXTransformAPI = false;
33
34 // Only used in www builds.
35 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.new-scheduler.js
+1
@@ -26,5 +26,6 @@ export const enableStableConcurrentModeAPIs = false;
26 export const warnAboutShorthandPropertyCollision = false;
27 export const warnAboutDeprecatedSetNativeProps = false;
28 export const enableEventAPI = false;
29 +export const enableJSXTransformAPI = false;
30
31 export const enableNewScheduler = true;
packages/shared/forks/ReactFeatureFlags.persistent.js
+1
@@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false;
29 export const warnAboutDeprecatedSetNativeProps = false;
30 export const enableEventAPI = false;
31 export const enableNewScheduler = false;
32 +export const enableJSXTransformAPI = false;
33
34 // Only used in www builds.
35 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.test-renderer.js
+1
@@ -29,6 +29,7 @@ export const enableSchedulerDebugging = false;
29 export const warnAboutDeprecatedSetNativeProps = false;
30 export const enableEventAPI = false;
31 export const enableNewScheduler = false;
32 +export const enableJSXTransformAPI = false;
33
34 // Only used in www builds.
35 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.test-renderer.www.js
+1
@@ -27,6 +27,7 @@ export const disableJavaScriptURLs = false;
27 export const disableYielding = false;
28 export const enableEventAPI = true;
29 export const enableNewScheduler = false;
30 +export const enableJSXTransformAPI = true;
31
32 // Only used in www builds.
33 export function addUserTimingListener() {
packages/shared/forks/ReactFeatureFlags.www-new-scheduler.js
+1
@@ -31,6 +31,7 @@ export {
31 } from './ReactFeatureFlags.www';
32
33 export const enableNewScheduler = true;
34 +export const enableJSXTransformAPI = true;
35
36 // Flow magic to verify the exports of this file match the original version.
37 // eslint-disable-next-line no-unused-vars
packages/shared/forks/ReactFeatureFlags.www.js
+2
@@ -74,6 +74,8 @@ function updateFlagOutsideOfReactCallStack() {
74
75 export const enableEventAPI = true;
76
77 +export const enableJSXTransformAPI = true;
78 +
79 // Flow magic to verify the exports of this file match the original version.
80 // eslint-disable-next-line no-unused-vars
81 type Check<_X, Y: _X, X: Y = _X> = null;