Compare commits
2 Commits
v16.13.1
...
fabric-foc
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
0e78755102 | ||
|
|
598739c2f4 |
@@ -5,8 +5,6 @@ const {
|
||||
esNextPaths,
|
||||
} = require('./scripts/shared/pathsByLanguageVersion');
|
||||
|
||||
const restrictedGlobals = require('confusing-browser-globals');
|
||||
|
||||
const OFF = 0;
|
||||
const ERROR = 2;
|
||||
|
||||
@@ -47,7 +45,6 @@ module.exports = {
|
||||
'no-bitwise': OFF,
|
||||
'no-inner-declarations': [ERROR, 'functions'],
|
||||
'no-multi-spaces': ERROR,
|
||||
'no-restricted-globals': [ERROR].concat(restrictedGlobals),
|
||||
'no-restricted-syntax': [ERROR, 'WithStatement'],
|
||||
'no-shadow': ERROR,
|
||||
'no-unused-expressions': ERROR,
|
||||
|
||||
7
.github/ISSUE_TEMPLATE/config.yml
vendored
7
.github/ISSUE_TEMPLATE/config.yml
vendored
@@ -1,7 +0,0 @@
|
||||
contact_links:
|
||||
- name: 📃 Documentation Issue
|
||||
url: https://github.com/reactjs/reactjs.org/issues/new
|
||||
about: This issue tracker is not for documentation issues. Please file documentation issues here.
|
||||
- name: 🤔 Questions and Help
|
||||
url: https://reactjs.org/community/support.html
|
||||
about: This issue tracker is not for support questions. Please refer to the React community's help and discussion forums.
|
||||
13
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
13
.github/ISSUE_TEMPLATE/documentation.md
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
---
|
||||
name: "📃 Documentation Issue"
|
||||
about: This issue tracker is not for documentation issues. Please file documentation issues at https://github.com/reactjs/reactjs.org.
|
||||
title: 'Docs: '
|
||||
labels: 'Resolution: Invalid'
|
||||
|
||||
---
|
||||
|
||||
🚨 This issue tracker is not for documentation issues. 🚨
|
||||
|
||||
The React website is hosted on a separate repository. You may let the
|
||||
team know about any issues with the documentation by opening an issue there:
|
||||
- https://github.com/reactjs/reactjs.org/issues/new
|
||||
29
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
29
.github/ISSUE_TEMPLATE/question.md
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
---
|
||||
name: "🤔 Questions and Help"
|
||||
about: This issue tracker is not for questions. Please ask questions at https://stackoverflow.com/questions/tagged/react.
|
||||
title: 'Question: '
|
||||
labels: 'Resolution: Invalid, Type: Question'
|
||||
|
||||
---
|
||||
|
||||
🚨 This issue tracker is not for questions. 🚨
|
||||
|
||||
As it happens, support requests that are created as issues are likely to be closed. We want to make sure you are able to find the help you seek. Please take a look at the following resources.
|
||||
|
||||
## Coding Questions
|
||||
|
||||
If you have a coding question related to React and React DOM, it might be better suited for Stack Overflow. It's a great place to browse through frequent questions about using React, as well as ask for help with specific questions.
|
||||
|
||||
https://stackoverflow.com/questions/tagged/react
|
||||
|
||||
## Talk to other React developers
|
||||
|
||||
There are many online forums which are a great place for discussion about best practices and application architecture as well as the future of React.
|
||||
|
||||
https://reactjs.org/community/support.html#popular-discussion-forums
|
||||
|
||||
## Proposals
|
||||
|
||||
If you'd like to discuss topics related to the future of React, or would like to propose a new feature or change before sending a pull request, please check out the discussions and proposals repository.
|
||||
|
||||
https://github.com/reactjs/rfcs
|
||||
@@ -1,3 +1,11 @@
|
||||
## [Unreleased]
|
||||
<details>
|
||||
<summary>
|
||||
Changes that have landed in master but are not yet released.
|
||||
Click to see more.
|
||||
</summary>
|
||||
</details>
|
||||
|
||||
## 16.12.0 (November 14, 2019)
|
||||
|
||||
### React DOM
|
||||
|
||||
@@ -171,7 +171,7 @@ function git(args) {
|
||||
|
||||
for (let i = 0; i < baseArtifactsInfo.length; i++) {
|
||||
const info = baseArtifactsInfo[i];
|
||||
if (info.path.endsWith('bundle-sizes.json')) {
|
||||
if (info.path === 'home/circleci/project/build/bundle-sizes.json') {
|
||||
const resultsResponse = await fetch(info.url);
|
||||
previousBuildResults = await resultsResponse.json();
|
||||
break;
|
||||
|
||||
@@ -11,7 +11,7 @@
|
||||
"classnames": "^2.2.5",
|
||||
"codemirror": "^5.40.0",
|
||||
"core-js": "^2.4.1",
|
||||
"jest-diff": "^25.1.0",
|
||||
"jest-diff": "^24.8.0",
|
||||
"prop-types": "^15.6.0",
|
||||
"query-string": "^4.2.3",
|
||||
"react": "^15.4.1",
|
||||
|
||||
@@ -8,9 +8,8 @@
|
||||
*/
|
||||
|
||||
let React;
|
||||
let DOMAct;
|
||||
let TestUtils;
|
||||
let TestRenderer;
|
||||
let TestAct;
|
||||
|
||||
global.__DEV__ = process.env.NODE_ENV !== 'production';
|
||||
|
||||
@@ -20,9 +19,8 @@ describe('unmocked scheduler', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
DOMAct = require('react-dom/test-utils').act;
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
TestRenderer = require('react-test-renderer');
|
||||
TestAct = TestRenderer.act;
|
||||
});
|
||||
|
||||
it('flushes work only outside the outermost act() corresponding to its own renderer', () => {
|
||||
@@ -34,8 +32,8 @@ describe('unmocked scheduler', () => {
|
||||
return null;
|
||||
}
|
||||
// in legacy mode, this tests whether an act only flushes its own effects
|
||||
TestAct(() => {
|
||||
DOMAct(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual([]);
|
||||
@@ -44,8 +42,8 @@ describe('unmocked scheduler', () => {
|
||||
|
||||
log = [];
|
||||
// for doublechecking, we flip it inside out, and assert on the outermost
|
||||
DOMAct(() => {
|
||||
TestAct(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual(['called']);
|
||||
@@ -61,9 +59,8 @@ describe('mocked scheduler', () => {
|
||||
require.requireActual('scheduler/unstable_mock')
|
||||
);
|
||||
React = require('react');
|
||||
DOMAct = require('react-dom/test-utils').act;
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
TestRenderer = require('react-test-renderer');
|
||||
TestAct = TestRenderer.act;
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -79,8 +76,8 @@ describe('mocked scheduler', () => {
|
||||
return null;
|
||||
}
|
||||
// with a mocked scheduler, this tests whether it flushes all work only on the outermost act
|
||||
TestAct(() => {
|
||||
DOMAct(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual([]);
|
||||
@@ -89,8 +86,8 @@ describe('mocked scheduler', () => {
|
||||
|
||||
log = [];
|
||||
// for doublechecking, we flip it inside out, and assert on the outermost
|
||||
DOMAct(() => {
|
||||
TestAct(() => {
|
||||
TestUtils.act(() => {
|
||||
TestRenderer.act(() => {
|
||||
TestRenderer.create(<Effecty />);
|
||||
});
|
||||
expect(log).toEqual([]);
|
||||
|
||||
@@ -10,9 +10,9 @@
|
||||
let React;
|
||||
let ReactDOM;
|
||||
let ReactART;
|
||||
let TestUtils;
|
||||
let ARTSVGMode;
|
||||
let ARTCurrentMode;
|
||||
let TestUtils;
|
||||
let TestRenderer;
|
||||
let ARTTest;
|
||||
|
||||
@@ -29,10 +29,10 @@ beforeEach(() => {
|
||||
jest.resetModules();
|
||||
React = require('react');
|
||||
ReactDOM = require('react-dom');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
ReactART = require('react-art');
|
||||
ARTSVGMode = require('art/modes/svg');
|
||||
ARTCurrentMode = require('art/modes/current');
|
||||
TestUtils = require('react-dom/test-utils');
|
||||
TestRenderer = require('react-test-renderer');
|
||||
|
||||
ARTCurrentMode.setCurrent(ARTSVGMode);
|
||||
@@ -71,7 +71,7 @@ beforeEach(() => {
|
||||
|
||||
it("doesn't warn when you use the right act + renderer: dom", () => {
|
||||
TestUtils.act(() => {
|
||||
ReactDOM.render(<App />, document.createElement('div'));
|
||||
TestUtils.renderIntoDocument(<App />);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -99,7 +99,7 @@ it('resets correctly across renderers', () => {
|
||||
it('warns when using the wrong act version - test + dom: render', () => {
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
ReactDOM.render(<App />, document.createElement('div'));
|
||||
TestUtils.renderIntoDocument(<App />);
|
||||
});
|
||||
}).toWarnDev(["It looks like you're using the wrong act()"], {
|
||||
withoutStack: true,
|
||||
@@ -113,7 +113,7 @@ it('warns when using the wrong act version - test + dom: updates', () => {
|
||||
setCtr = _setCtr;
|
||||
return ctr;
|
||||
}
|
||||
ReactDOM.render(<Counter />, document.createElement('div'));
|
||||
TestUtils.renderIntoDocument(<Counter />);
|
||||
expect(() => {
|
||||
TestRenderer.act(() => {
|
||||
setCtr(1);
|
||||
@@ -159,7 +159,7 @@ it('warns when using the wrong act version - dom + test: updates', () => {
|
||||
|
||||
it('does not warn when nesting react-act inside react-dom', () => {
|
||||
TestUtils.act(() => {
|
||||
ReactDOM.render(<ARTTest />, document.createElement('div'));
|
||||
TestUtils.renderIntoDocument(<ARTTest />);
|
||||
});
|
||||
});
|
||||
|
||||
|
||||
@@ -86,7 +86,6 @@ class Header extends React.Component {
|
||||
<option value="/mouse-events">Mouse Events</option>
|
||||
<option value="/selection-events">Selection Events</option>
|
||||
<option value="/suspense">Suspense</option>
|
||||
<option value="/form-state">Form State</option>
|
||||
</select>
|
||||
</label>
|
||||
<label htmlFor="global_version">
|
||||
|
||||
@@ -1,60 +0,0 @@
|
||||
import Fixture from '../../Fixture';
|
||||
const React = window.React;
|
||||
|
||||
class ControlledFormFixture extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
this.state = {name: '', email: ''};
|
||||
|
||||
this.handleEmailChange = this.handleEmailChange.bind(this);
|
||||
this.handleNameChange = this.handleNameChange.bind(this);
|
||||
}
|
||||
|
||||
handleEmailChange(event) {
|
||||
this.setState({email: event.target.value});
|
||||
}
|
||||
|
||||
handleNameChange(event) {
|
||||
this.setState({name: event.target.value});
|
||||
}
|
||||
|
||||
render() {
|
||||
return (
|
||||
<Fixture>
|
||||
<form>
|
||||
<label>
|
||||
Name:
|
||||
<input
|
||||
type="text"
|
||||
value={this.state.name}
|
||||
onChange={this.handleNameChange}
|
||||
name="name"
|
||||
x-autocompletetype="name"
|
||||
/>
|
||||
</label>
|
||||
<br />
|
||||
<label>
|
||||
Email:
|
||||
<input
|
||||
type="text"
|
||||
value={this.state.email}
|
||||
onChange={this.handleEmailChange}
|
||||
name="email"
|
||||
x-autocompletetype="email"
|
||||
/>
|
||||
</label>
|
||||
</form>
|
||||
<br />
|
||||
<div>
|
||||
<span>States</span>
|
||||
<br />
|
||||
<span>Name: {this.state.name}</span>
|
||||
<br />
|
||||
<span>Email: {this.state.email}</span>
|
||||
</div>
|
||||
</Fixture>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
export default ControlledFormFixture;
|
||||
@@ -1,60 +0,0 @@
|
||||
import FixtureSet from '../../FixtureSet';
|
||||
import TestCase from '../../TestCase';
|
||||
import ControlledFormFixture from './ControlledFormFixture';
|
||||
const React = window.React;
|
||||
|
||||
export default class FormStateCases extends React.Component {
|
||||
render() {
|
||||
return (
|
||||
<FixtureSet title="Form State">
|
||||
<TestCase
|
||||
title="Form state autofills from browser"
|
||||
description="Form start should autofill/autocomplete if user has autocomplete/autofill information saved. The user may need to set-up autofill or autocomplete with their specific browser.">
|
||||
<TestCase.Steps>
|
||||
<li>
|
||||
Set up autofill/autocomplete for your browser.
|
||||
<br />
|
||||
Instructions:
|
||||
<ul>
|
||||
<li>
|
||||
<SafeLink
|
||||
href="https://support.google.com/chrome/answer/142893?co=GENIE.Platform%3DDesktop&hl=en"
|
||||
text="Google Chrome"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<SafeLink
|
||||
href="https://support.mozilla.org/en-US/kb/autofill-logins-firefox"
|
||||
text="Mozilla FireFox"
|
||||
/>
|
||||
</li>
|
||||
<li>
|
||||
<SafeLink
|
||||
href="https://support.microsoft.com/en-us/help/4027718/microsoft-edge-automatically-fill-info"
|
||||
text="Microsoft Edge"
|
||||
/>
|
||||
</li>
|
||||
</ul>
|
||||
</li>
|
||||
<li>Click into any input.</li>
|
||||
<li>Select any autofill option.</li>
|
||||
</TestCase.Steps>
|
||||
<TestCase.ExpectedResult>
|
||||
Autofill options should appear when clicking into fields. Selected
|
||||
autofill options should change state (shown underneath, under
|
||||
"States").
|
||||
</TestCase.ExpectedResult>
|
||||
<ControlledFormFixture />
|
||||
</TestCase>
|
||||
</FixtureSet>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const SafeLink = ({text, href}) => {
|
||||
return (
|
||||
<a target="_blank" rel="noreferrer" href={href}>
|
||||
{text}
|
||||
</a>
|
||||
);
|
||||
};
|
||||
@@ -1,7 +1,7 @@
|
||||
// copied from scripts/jest/matchers/toWarnDev.js
|
||||
'use strict';
|
||||
|
||||
const jestDiff = require('jest-diff').default;
|
||||
const jestDiff = require('jest-diff');
|
||||
const util = require('util');
|
||||
|
||||
function shouldIgnoreConsoleError(format, args) {
|
||||
|
||||
@@ -6,20 +6,14 @@
|
||||
version "7.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.0.0.tgz#856446641620c1c5f0ca775621d478324ebd1f52"
|
||||
|
||||
"@jest/types@^25.1.0":
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.1.0.tgz#b26831916f0d7c381e11dbb5e103a72aed1b4395"
|
||||
integrity sha512-VpOtt7tCrgvamWZh1reVsGADujKigBUFTi19mlRjqEGsE8qH4r3s+skY33dNdXOwyZIvuftZ5tqdF1IgsMejMA==
|
||||
"@jest/types@^24.8.0":
|
||||
version "24.8.0"
|
||||
resolved "https://registry.yarnpkg.com/@jest/types/-/types-24.8.0.tgz#f31e25948c58f0abd8c845ae26fcea1491dea7ad"
|
||||
integrity sha512-g17UxVr2YfBtaMUxn9u/4+siG1ptg9IGYAYwvpwn61nBg779RXnjE/m7CxYcIzEt0AbHZZAHSEZNhkE2WxURVg==
|
||||
dependencies:
|
||||
"@types/istanbul-lib-coverage" "^2.0.0"
|
||||
"@types/istanbul-reports" "^1.1.1"
|
||||
"@types/yargs" "^15.0.0"
|
||||
chalk "^3.0.0"
|
||||
|
||||
"@types/color-name@^1.1.1":
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/color-name/-/color-name-1.1.1.tgz#1c1261bbeaa10a8055bbc5d8ab84b7b2afc846a0"
|
||||
integrity sha512-rr+OQyAjxze7GgWrSaJwydHStIhHq2lvY3BOC2Mj7KnzI7XK0Uw1TOOdI9lDoajEbSWLiYgoo4f1R51erQfhPQ==
|
||||
"@types/yargs" "^12.0.9"
|
||||
|
||||
"@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0":
|
||||
version "2.0.1"
|
||||
@@ -41,17 +35,10 @@
|
||||
"@types/istanbul-lib-coverage" "*"
|
||||
"@types/istanbul-lib-report" "*"
|
||||
|
||||
"@types/yargs-parser@*":
|
||||
version "15.0.0"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs-parser/-/yargs-parser-15.0.0.tgz#cb3f9f741869e20cce330ffbeb9271590483882d"
|
||||
integrity sha512-FA/BWv8t8ZWJ+gEOnLLd8ygxH/2UFbAvgEonyfN6yWGLKc7zVjbpl2Y4CTjid9h2RfgPP6SEt6uHwEOply00yw==
|
||||
|
||||
"@types/yargs@^15.0.0":
|
||||
version "15.0.1"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-15.0.1.tgz#9266a9d7be68cfcc982568211085a49a277f7c96"
|
||||
integrity sha512-sYlwNU7zYi6eZbMzFvG6eHD7VsEvFdoDtlD7eI1JTg7YNnuguzmiGsc6MPSq5l8n+h21AsNof0je+9sgOe4+dg==
|
||||
dependencies:
|
||||
"@types/yargs-parser" "*"
|
||||
"@types/yargs@^12.0.9":
|
||||
version "12.0.12"
|
||||
resolved "https://registry.yarnpkg.com/@types/yargs/-/yargs-12.0.12.tgz#45dd1d0638e8c8f153e87d296907659296873916"
|
||||
integrity sha512-SOhuU4wNBxhhTHxYaiG5NY4HBhDIDnJF60GU+2LqHAdKKer86//e4yg69aENCtQ04n0ovz+tq2YPME5t5yp4pw==
|
||||
|
||||
abab@^1.0.3:
|
||||
version "1.0.3"
|
||||
@@ -187,10 +174,10 @@ ansi-regex@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
|
||||
|
||||
ansi-regex@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
|
||||
integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
|
||||
ansi-regex@^4.0.0:
|
||||
version "4.1.0"
|
||||
resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-4.1.0.tgz#8b9f8f08cf1acb843756a839ca8c7e3168c51997"
|
||||
integrity sha512-1apePfXM1UOSqw0o9IiFAovVz9M5S1Dg+4TrDwfMewQ6p/rmMueb7tWZjQ1rx4Loy1ArBggoqGpfqqdI4rondg==
|
||||
|
||||
ansi-styles@^2.2.1:
|
||||
version "2.2.1"
|
||||
@@ -202,13 +189,12 @@ ansi-styles@^3.0.0, ansi-styles@^3.1.0:
|
||||
dependencies:
|
||||
color-convert "^1.9.0"
|
||||
|
||||
ansi-styles@^4.0.0, ansi-styles@^4.1.0:
|
||||
version "4.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-4.2.1.tgz#90ae75c424d008d2624c5bf29ead3177ebfcf359"
|
||||
integrity sha512-9VGjrMsG1vePxcSweQsN20KY/c4zN0h9fLjqAbwbPfahM3t+NL+M9HC8xeXG2I8pX5NoamTGNuomEUFI7fcUjA==
|
||||
ansi-styles@^3.2.0, ansi-styles@^3.2.1:
|
||||
version "3.2.1"
|
||||
resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
|
||||
integrity sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==
|
||||
dependencies:
|
||||
"@types/color-name" "^1.1.1"
|
||||
color-convert "^2.0.1"
|
||||
color-convert "^1.9.0"
|
||||
|
||||
anymatch@^1.3.0:
|
||||
version "1.3.0"
|
||||
@@ -1521,13 +1507,14 @@ chalk@^2.0.0, chalk@^2.1.0:
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^4.0.0"
|
||||
|
||||
chalk@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-3.0.0.tgz#3f73c2bf526591f574cc492c51e2456349f844e4"
|
||||
integrity sha512-4D3B6Wf41KOYRFdszmDqMCGq5VV/uMAB273JILmO+3jAlh8X4qDtdtgCR3fxtbLEMzSx22QdhnDcJvu2u1fVwg==
|
||||
chalk@^2.0.1:
|
||||
version "2.4.2"
|
||||
resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
|
||||
integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==
|
||||
dependencies:
|
||||
ansi-styles "^4.1.0"
|
||||
supports-color "^7.1.0"
|
||||
ansi-styles "^3.2.1"
|
||||
escape-string-regexp "^1.0.5"
|
||||
supports-color "^5.3.0"
|
||||
|
||||
chokidar@^1.6.0, chokidar@^1.7.0:
|
||||
version "1.7.0"
|
||||
@@ -1639,22 +1626,10 @@ color-convert@^1.9.0:
|
||||
dependencies:
|
||||
color-name "^1.1.1"
|
||||
|
||||
color-convert@^2.0.1:
|
||||
version "2.0.1"
|
||||
resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-2.0.1.tgz#72d3a68d598c9bdb3af2ad1e84f21d896abd4de3"
|
||||
integrity sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==
|
||||
dependencies:
|
||||
color-name "~1.1.4"
|
||||
|
||||
color-name@^1.0.0, color-name@^1.1.1:
|
||||
version "1.1.1"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.1.tgz#4b1415304cf50028ea81643643bd82ea05803689"
|
||||
|
||||
color-name@~1.1.4:
|
||||
version "1.1.4"
|
||||
resolved "https://registry.yarnpkg.com/color-name/-/color-name-1.1.4.tgz#c2a09a87acbde69543de6f63fa3995c826c536a2"
|
||||
integrity sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==
|
||||
|
||||
color-string@^0.3.0:
|
||||
version "0.3.0"
|
||||
resolved "https://registry.yarnpkg.com/color-string/-/color-string-0.3.0.tgz#27d46fb67025c5c2fa25993bfbf579e47841b991"
|
||||
@@ -2138,10 +2113,10 @@ detect-port-alt@1.1.3:
|
||||
address "^1.0.1"
|
||||
debug "^2.6.0"
|
||||
|
||||
diff-sequences@^25.1.0:
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-25.1.0.tgz#fd29a46f1c913fd66c22645dc75bffbe43051f32"
|
||||
integrity sha512-nFIfVk5B/NStCsJ+zaPO4vYuLjlzQ6uFvPxzYyHlejNZ/UGa7G/n7peOXVrVNvRuyfstt+mZQYGpjxg9Z6N8Kw==
|
||||
diff-sequences@^24.3.0:
|
||||
version "24.3.0"
|
||||
resolved "https://registry.yarnpkg.com/diff-sequences/-/diff-sequences-24.3.0.tgz#0f20e8a1df1abddaf4d9c226680952e64118b975"
|
||||
integrity sha512-xLqpez+Zj9GKSnPWS0WZw1igGocZ+uua8+y+5dDNTT934N3QuY1sp2LkHzwiaYQGz60hMq0pjAshdeXm5VUOEw==
|
||||
|
||||
diff@^3.2.0:
|
||||
version "3.3.0"
|
||||
@@ -3205,10 +3180,10 @@ has-flag@^2.0.0:
|
||||
version "2.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-2.0.0.tgz#e8207af1cc7b30d446cc70b734b5e8be18f88d51"
|
||||
|
||||
has-flag@^4.0.0:
|
||||
version "4.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
|
||||
integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
|
||||
has-flag@^3.0.0:
|
||||
version "3.0.0"
|
||||
resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-3.0.0.tgz#b5d454dc2199ae225699f3467e5a07f3b955bafd"
|
||||
integrity sha1-tdRU3CGZriJWmfNGfloH87lVuv0=
|
||||
|
||||
has-unicode@^2.0.0:
|
||||
version "2.0.1"
|
||||
@@ -3861,15 +3836,15 @@ jest-diff@^20.0.3:
|
||||
jest-matcher-utils "^20.0.3"
|
||||
pretty-format "^20.0.3"
|
||||
|
||||
jest-diff@^25.1.0:
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.1.0.tgz#58b827e63edea1bc80c1de952b80cec9ac50e1ad"
|
||||
integrity sha512-nepXgajT+h017APJTreSieh4zCqnSHEJ1iT8HDlewu630lSJ4Kjjr9KNzm+kzGwwcpsDE6Snx1GJGzzsefaEHw==
|
||||
jest-diff@^24.8.0:
|
||||
version "24.8.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-24.8.0.tgz#146435e7d1e3ffdf293d53ff97e193f1d1546172"
|
||||
integrity sha512-wxetCEl49zUpJ/bvUmIFjd/o52J+yWcoc5ZyPq4/W1LUKGEhRYDIbP1KcF6t+PvqNrGAFk4/JhtxDq/Nnzs66g==
|
||||
dependencies:
|
||||
chalk "^3.0.0"
|
||||
diff-sequences "^25.1.0"
|
||||
jest-get-type "^25.1.0"
|
||||
pretty-format "^25.1.0"
|
||||
chalk "^2.0.1"
|
||||
diff-sequences "^24.3.0"
|
||||
jest-get-type "^24.8.0"
|
||||
pretty-format "^24.8.0"
|
||||
|
||||
jest-docblock@^20.0.3:
|
||||
version "20.0.3"
|
||||
@@ -3890,10 +3865,10 @@ jest-environment-node@^20.0.3:
|
||||
jest-mock "^20.0.3"
|
||||
jest-util "^20.0.3"
|
||||
|
||||
jest-get-type@^25.1.0:
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.1.0.tgz#1cfe5fc34f148dc3a8a3b7275f6b9ce9e2e8a876"
|
||||
integrity sha512-yWkBnT+5tMr8ANB6V+OjmrIJufHtCAqI5ic2H40v+tRqxDmE0PGnIiTyvRWFOMtmVHYpwRqyazDbTnhpjsGvLw==
|
||||
jest-get-type@^24.8.0:
|
||||
version "24.8.0"
|
||||
resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-24.8.0.tgz#a7440de30b651f5a70ea3ed7ff073a32dfe646fc"
|
||||
integrity sha512-RR4fo8jEmMD9zSz2nLbs2j0zvPpk/KCEz3a62jJWbd2ayNo0cb+KFRxPHVhE4ZmgGJEQp0fosmNz84IfqM8cMQ==
|
||||
|
||||
jest-haste-map@^20.0.4:
|
||||
version "20.0.5"
|
||||
@@ -5360,15 +5335,15 @@ pretty-format@^20.0.3:
|
||||
ansi-regex "^2.1.1"
|
||||
ansi-styles "^3.0.0"
|
||||
|
||||
pretty-format@^25.1.0:
|
||||
version "25.1.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.1.0.tgz#ed869bdaec1356fc5ae45de045e2c8ec7b07b0c8"
|
||||
integrity sha512-46zLRSGLd02Rp+Lhad9zzuNZ+swunitn8zIpfD2B4OPCRLXbM87RJT2aBLBWYOznNUML/2l/ReMyWNC80PJBUQ==
|
||||
pretty-format@^24.8.0:
|
||||
version "24.8.0"
|
||||
resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-24.8.0.tgz#8dae7044f58db7cb8be245383b565a963e3c27f2"
|
||||
integrity sha512-P952T7dkrDEplsR+TuY7q3VXDae5Sr7zmQb12JU/NDQa/3CH7/QW0yvqLcGN6jL+zQFKaoJcPc+yJxMTGmosqw==
|
||||
dependencies:
|
||||
"@jest/types" "^25.1.0"
|
||||
ansi-regex "^5.0.0"
|
||||
ansi-styles "^4.0.0"
|
||||
react-is "^16.12.0"
|
||||
"@jest/types" "^24.8.0"
|
||||
ansi-regex "^4.0.0"
|
||||
ansi-styles "^3.2.0"
|
||||
react-is "^16.8.4"
|
||||
|
||||
private@^0.1.6:
|
||||
version "0.1.6"
|
||||
@@ -5556,10 +5531,10 @@ react-error-overlay@^1.0.10:
|
||||
settle-promise "1.0.0"
|
||||
source-map "0.5.6"
|
||||
|
||||
react-is@^16.12.0:
|
||||
version "16.12.0"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.12.0.tgz#2cc0fe0fba742d97fd527c42a13bec4eeb06241c"
|
||||
integrity sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q==
|
||||
react-is@^16.8.4:
|
||||
version "16.8.6"
|
||||
resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.8.6.tgz#5bbc1e2d29141c9fbdfed456343fe2bc430a6a16"
|
||||
integrity sha512-aUk3bHfZ2bRSVFFbbeVS4i+lNPZr3/WM5jT2J5omUVV1zzcs1nAaf3l51ctA5FFvCRbhrH0bdAsRRQddFJZPtA==
|
||||
|
||||
react-scripts@^1.0.11:
|
||||
version "1.0.11"
|
||||
@@ -6379,12 +6354,12 @@ supports-color@^4.0.0, supports-color@^4.2.1:
|
||||
dependencies:
|
||||
has-flag "^2.0.0"
|
||||
|
||||
supports-color@^7.1.0:
|
||||
version "7.1.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-7.1.0.tgz#68e32591df73e25ad1c4b49108a2ec507962bfd1"
|
||||
integrity sha512-oRSIpR8pxT1Wr2FquTNnGet79b3BWljqOuoW/h4oBhxJ/HUbX5nX6JSruTkvXDCFMwDPvsaTTbvMLKZWSy0R5g==
|
||||
supports-color@^5.3.0:
|
||||
version "5.5.0"
|
||||
resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
|
||||
integrity sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==
|
||||
dependencies:
|
||||
has-flag "^4.0.0"
|
||||
has-flag "^3.0.0"
|
||||
|
||||
svgo@^0.7.0:
|
||||
version "0.7.1"
|
||||
|
||||
115
package.json
115
package.json
@@ -4,49 +4,48 @@
|
||||
"packages/*"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@babel/cli": "^7.8.0",
|
||||
"@babel/code-frame": "^7.8.0",
|
||||
"@babel/core": "^7.8.0",
|
||||
"@babel/helper-module-imports": "^7.8.0",
|
||||
"@babel/parser": "^7.8.0",
|
||||
"@babel/plugin-external-helpers": "^7.8.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.8.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.8.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.8.0",
|
||||
"@babel/plugin-syntax-jsx": "^7.8.0",
|
||||
"@babel/plugin-transform-arrow-functions": "^7.8.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.8.0",
|
||||
"@babel/plugin-transform-block-scoped-functions": "^7.8.0",
|
||||
"@babel/plugin-transform-block-scoping": "^7.8.0",
|
||||
"@babel/plugin-transform-classes": "^7.8.0",
|
||||
"@babel/plugin-transform-computed-properties": "^7.8.0",
|
||||
"@babel/plugin-transform-destructuring": "^7.8.0",
|
||||
"@babel/plugin-transform-for-of": "^7.8.0",
|
||||
"@babel/plugin-transform-literals": "^7.8.0",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.8.0",
|
||||
"@babel/plugin-transform-object-super": "^7.8.0",
|
||||
"@babel/plugin-transform-parameters": "^7.8.0",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.8.0",
|
||||
"@babel/plugin-transform-shorthand-properties": "^7.8.0",
|
||||
"@babel/plugin-transform-spread": "^7.8.0",
|
||||
"@babel/plugin-transform-template-literals": "^7.8.0",
|
||||
"@babel/preset-flow": "^7.8.0",
|
||||
"@babel/preset-react": "^7.8.0",
|
||||
"@babel/traverse": "^7.8.0",
|
||||
"@mattiasbuelens/web-streams-polyfill": "^0.3.2",
|
||||
"art": "0.10.1",
|
||||
"@babel/cli": "^7.0.0",
|
||||
"@babel/code-frame": "^7.0.0",
|
||||
"@babel/core": "^7.0.0",
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"@babel/parser": "^7.0.0",
|
||||
"@babel/plugin-external-helpers": "^7.0.0",
|
||||
"@babel/plugin-proposal-class-properties": "^7.0.0",
|
||||
"@babel/plugin-proposal-object-rest-spread": "^7.0.0",
|
||||
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
|
||||
"@babel/plugin-syntax-jsx": "^7.2.0",
|
||||
"@babel/plugin-transform-arrow-functions": "^7.0.0",
|
||||
"@babel/plugin-transform-async-to-generator": "^7.0.0",
|
||||
"@babel/plugin-transform-block-scoped-functions": "^7.0.0",
|
||||
"@babel/plugin-transform-block-scoping": "^7.0.0",
|
||||
"@babel/plugin-transform-classes": "^7.0.0",
|
||||
"@babel/plugin-transform-computed-properties": "^7.0.0",
|
||||
"@babel/plugin-transform-destructuring": "^7.0.0",
|
||||
"@babel/plugin-transform-for-of": "^7.0.0",
|
||||
"@babel/plugin-transform-literals": "^7.0.0",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.0.0",
|
||||
"@babel/plugin-transform-object-super": "^7.0.0",
|
||||
"@babel/plugin-transform-parameters": "^7.0.0",
|
||||
"@babel/plugin-transform-react-jsx-source": "^7.0.0",
|
||||
"@babel/plugin-transform-shorthand-properties": "^7.0.0",
|
||||
"@babel/plugin-transform-spread": "^7.0.0",
|
||||
"@babel/plugin-transform-template-literals": "^7.0.0",
|
||||
"@babel/preset-flow": "^7.0.0",
|
||||
"@babel/preset-react": "^7.0.0",
|
||||
"@babel/traverse": "^7.0.0",
|
||||
"@mattiasbuelens/web-streams-polyfill": "0.1.0",
|
||||
"art": "^0.10.1",
|
||||
"babel-eslint": "^10.0.3",
|
||||
"babel-plugin-syntax-trailing-function-commas": "^6.5.0",
|
||||
"chalk": "^3.0.0",
|
||||
"chalk": "^1.1.3",
|
||||
"cli-table": "^0.3.1",
|
||||
"coffee-script": "^1.12.7",
|
||||
"confusing-browser-globals": "^1.0.9",
|
||||
"core-js": "^3.6.4",
|
||||
"coveralls": "^3.0.9",
|
||||
"coffee-script": "^1.8.0",
|
||||
"core-js": "^2.2.1",
|
||||
"coveralls": "^2.11.6",
|
||||
"create-react-class": "^15.6.3",
|
||||
"cross-env": "^6.0.3",
|
||||
"danger": "^9.2.10",
|
||||
"error-stack-parser": "^2.0.6",
|
||||
"cross-env": "^5.1.1",
|
||||
"danger": "^9.1.8",
|
||||
"error-stack-parser": "^2.0.2",
|
||||
"eslint": "^6.8.0",
|
||||
"eslint-config-fbjs": "^1.1.1",
|
||||
"eslint-config-prettier": "^6.9.0",
|
||||
@@ -56,40 +55,40 @@
|
||||
"eslint-plugin-no-for-of-loops": "^1.0.0",
|
||||
"eslint-plugin-react": "^6.7.1",
|
||||
"eslint-plugin-react-internal": "link:./scripts/eslint-rules",
|
||||
"fbjs-scripts": "0.8.3",
|
||||
"filesize": "^6.0.1",
|
||||
"flow-bin": "0.97",
|
||||
"glob": "^7.1.6",
|
||||
"fbjs-scripts": "^0.8.3",
|
||||
"filesize": "^3.5.6",
|
||||
"flow-bin": "^0.84.0",
|
||||
"glob": "^6.0.4",
|
||||
"glob-stream": "^6.1.0",
|
||||
"google-closure-compiler": "^20200112.0.0",
|
||||
"gzip-size": "^5.1.1",
|
||||
"google-closure-compiler": "20190301.0.0",
|
||||
"gzip-size": "^3.0.0",
|
||||
"jasmine-check": "^1.0.0-rc.0",
|
||||
"jest": "^25.1.0",
|
||||
"jest-diff": "^25.1.0",
|
||||
"jest": "^24.9.0",
|
||||
"jest-diff": "^24.9.0",
|
||||
"jest-snapshot-serializer-raw": "^1.1.0",
|
||||
"minimatch": "^3.0.4",
|
||||
"minimist": "^1.2.0",
|
||||
"mkdirp": "^0.5.1",
|
||||
"ncp": "^2.0.0",
|
||||
"object-assign": "^4.1.1",
|
||||
"pacote": "^10.3.0",
|
||||
"pacote": "^9.5.6",
|
||||
"prettier": "1.19.1",
|
||||
"prop-types": "^15.6.2",
|
||||
"random-seed": "^0.3.0",
|
||||
"react-lifecycles-compat": "^3.0.4",
|
||||
"rimraf": "^3.0.0",
|
||||
"rollup": "^1.19.4",
|
||||
"react-lifecycles-compat": "^3.0.2",
|
||||
"rimraf": "^2.6.1",
|
||||
"rollup": "^0.52.1",
|
||||
"rollup-plugin-babel": "^4.0.1",
|
||||
"rollup-plugin-commonjs": "^9.3.4",
|
||||
"rollup-plugin-commonjs": "^8.2.6",
|
||||
"rollup-plugin-node-resolve": "^2.1.1",
|
||||
"rollup-plugin-prettier": "^0.6.0",
|
||||
"rollup-plugin-replace": "^2.2.0",
|
||||
"rollup-plugin-prettier": "^0.3.0",
|
||||
"rollup-plugin-replace": "^2.0.0",
|
||||
"rollup-plugin-strip-banner": "^0.2.0",
|
||||
"semver": "^7.1.1",
|
||||
"semver": "^5.5.0",
|
||||
"targz": "^1.0.1",
|
||||
"through2": "^3.0.1",
|
||||
"tmp": "^0.1.0",
|
||||
"typescript": "^3.7.5",
|
||||
"through2": "^2.0.0",
|
||||
"tmp": "~0.0.28",
|
||||
"typescript": "^3.7.4",
|
||||
"webpack": "^4.41.2"
|
||||
},
|
||||
"devEngines": {
|
||||
|
||||
@@ -0,0 +1,392 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its affiliates.
|
||||
*
|
||||
* This source code is licensed under the MIT license found in the
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
/* eslint-disable quotes */
|
||||
'use strict';
|
||||
|
||||
const babel = require('@babel/core');
|
||||
const codeFrame = require('@babel/code-frame');
|
||||
const {wrap} = require('jest-snapshot-serializer-raw');
|
||||
|
||||
function transform(input, options) {
|
||||
return wrap(
|
||||
babel.transform(input, {
|
||||
configFile: false,
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-jsx',
|
||||
'@babel/plugin-transform-arrow-functions',
|
||||
...(options && options.development
|
||||
? [
|
||||
'@babel/plugin-transform-react-jsx-source',
|
||||
'@babel/plugin-transform-react-jsx-self',
|
||||
]
|
||||
: []),
|
||||
[
|
||||
'./packages/babel-plugin-react-jsx',
|
||||
{
|
||||
development: __DEV__,
|
||||
useBuiltIns: true,
|
||||
useCreateElement: true,
|
||||
...options,
|
||||
},
|
||||
],
|
||||
],
|
||||
}).code
|
||||
);
|
||||
}
|
||||
|
||||
describe('transform react to jsx', () => {
|
||||
it('fragment with no children', () => {
|
||||
expect(transform(`var x = <></>`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('React.Fragment to set keys and source', () => {
|
||||
expect(
|
||||
transform(`var x = <React.Fragment key='foo'><div /></React.Fragment>`, {
|
||||
development: true,
|
||||
})
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('normal fragments not to set key and source', () => {
|
||||
expect(
|
||||
transform(`var x = <><div /></>`, {
|
||||
development: true,
|
||||
})
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should properly handle comments adjacent to children', () => {
|
||||
expect(
|
||||
transform(`
|
||||
var x = (
|
||||
<div>
|
||||
{/* A comment at the beginning */}
|
||||
{/* A second comment at the beginning */}
|
||||
<span>
|
||||
{/* A nested comment */}
|
||||
</span>
|
||||
{/* A sandwiched comment */}
|
||||
<br />
|
||||
{/* A comment at the end */}
|
||||
{/* A second comment at the end */}
|
||||
</div>
|
||||
);
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('adds appropriate new lines when using spread attribute', () => {
|
||||
expect(transform(`<Component {...props} sound="moo" />`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('arrow functions', () => {
|
||||
expect(
|
||||
transform(`
|
||||
var foo = function () {
|
||||
return () => <this />;
|
||||
};
|
||||
|
||||
var bar = function () {
|
||||
return () => <this.foo />;
|
||||
};
|
||||
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('assignment', () => {
|
||||
expect(
|
||||
transform(`var div = <Component {...props} foo="bar" />`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('concatenates adjacent string literals', () => {
|
||||
expect(
|
||||
transform(`
|
||||
var x =
|
||||
<div>
|
||||
foo
|
||||
{"bar"}
|
||||
baz
|
||||
<div>
|
||||
buz
|
||||
bang
|
||||
</div>
|
||||
qux
|
||||
{null}
|
||||
quack
|
||||
</div>
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should allow constructor as prop', () => {
|
||||
expect(transform(`<Component constructor="foo" />;`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should allow deeper js namespacing', () => {
|
||||
expect(
|
||||
transform(`<Namespace.DeepNamespace.Component />;`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should allow elements as attributes', () => {
|
||||
expect(transform(`<div attr=<div /> />`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should allow js namespacing', () => {
|
||||
expect(transform(`<Namespace.Component />;`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should allow nested fragments', () => {
|
||||
expect(
|
||||
transform(`
|
||||
<div>
|
||||
< >
|
||||
<>
|
||||
<span>Hello</span>
|
||||
<span>world</span>
|
||||
</>
|
||||
<>
|
||||
<span>Goodbye</span>
|
||||
<span>world</span>
|
||||
</>
|
||||
</>
|
||||
</div>
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should avoid wrapping in extra parens if not needed', () => {
|
||||
expect(
|
||||
transform(`
|
||||
var x = <div>
|
||||
<Component />
|
||||
</div>;
|
||||
|
||||
var x = <div>
|
||||
{props.children}
|
||||
</div>;
|
||||
|
||||
var x = <Composite>
|
||||
{props.children}
|
||||
</Composite>;
|
||||
|
||||
var x = <Composite>
|
||||
<Composite2 />
|
||||
</Composite>;
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should convert simple tags', () => {
|
||||
expect(transform(`var x = <div></div>;`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should convert simple text', () => {
|
||||
expect(transform(`var x = <div>text</div>;`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should disallow spread children', () => {
|
||||
let _error;
|
||||
const code = `<div>{...children}</div>;`;
|
||||
try {
|
||||
transform(code);
|
||||
} catch (error) {
|
||||
_error = error;
|
||||
}
|
||||
expect(_error).toEqual(
|
||||
new SyntaxError(
|
||||
'undefined: Spread children are not supported in React.' +
|
||||
'\n' +
|
||||
codeFrame.codeFrameColumns(
|
||||
code,
|
||||
{start: {line: 1, column: 6}},
|
||||
{highlightCode: true}
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('should escape xhtml jsxattribute', () => {
|
||||
expect(
|
||||
transform(`
|
||||
<div id="wôw" />;
|
||||
<div id="\w" />;
|
||||
<div id="w < w" />;
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should escape xhtml jsxtext', () => {
|
||||
/* eslint-disable no-irregular-whitespace */
|
||||
expect(
|
||||
transform(`
|
||||
<div>wow</div>;
|
||||
<div>wôw</div>;
|
||||
|
||||
<div>w & w</div>;
|
||||
<div>w & w</div>;
|
||||
|
||||
<div>w w</div>;
|
||||
<div>this should not parse as unicode: \u00a0</div>;
|
||||
<div>this should parse as nbsp: </div>;
|
||||
<div>this should parse as unicode: {'\u00a0 '}</div>;
|
||||
|
||||
<div>w < w</div>;
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
/*eslint-enable */
|
||||
});
|
||||
|
||||
it('should handle attributed elements', () => {
|
||||
expect(
|
||||
transform(`
|
||||
var HelloMessage = React.createClass({
|
||||
render: function() {
|
||||
return <div>Hello {this.props.name}</div>;
|
||||
}
|
||||
});
|
||||
|
||||
React.render(<HelloMessage name={
|
||||
<span>
|
||||
Sebastian
|
||||
</span>
|
||||
} />, mountNode);
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should handle has own property correctly', () => {
|
||||
expect(
|
||||
transform(`<hasOwnProperty>testing</hasOwnProperty>;`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should have correct comma in nested children', () => {
|
||||
expect(
|
||||
transform(`
|
||||
var x = <div>
|
||||
<div><br /></div>
|
||||
<Component>{foo}<br />{bar}</Component>
|
||||
<br />
|
||||
</div>;
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should insert commas after expressions before whitespace', () => {
|
||||
expect(
|
||||
transform(`
|
||||
var x =
|
||||
<div
|
||||
attr1={
|
||||
"foo" + "bar"
|
||||
}
|
||||
attr2={
|
||||
"foo" + "bar" +
|
||||
|
||||
"baz" + "bug"
|
||||
}
|
||||
attr3={
|
||||
"foo" + "bar" +
|
||||
"baz" + "bug"
|
||||
}
|
||||
attr4="baz">
|
||||
</div>
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not add quotes to identifier names', () => {
|
||||
expect(
|
||||
transform(`var e = <F aaa new const var default foo-bar/>;`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not strip nbsp even couple with other whitespace', () => {
|
||||
expect(transform(`<div> </div>;`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should not strip tags with a single child of nbsp', () => {
|
||||
expect(transform(`<div> </div>;`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should properly handle comments between props', () => {
|
||||
expect(
|
||||
transform(`
|
||||
var x = (
|
||||
<div
|
||||
/* a multi-line
|
||||
comment */
|
||||
attr1="foo">
|
||||
<span // a double-slash comment
|
||||
attr2="bar"
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should quote jsx attributes', () => {
|
||||
expect(
|
||||
transform(`<button data-value='a value'>Button</button>`)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should support xml namespaces if flag', () => {
|
||||
expect(
|
||||
transform('<f:image n:attr />', {throwIfNamespace: false})
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('should throw error namespaces if not flag', () => {
|
||||
let _error;
|
||||
const code = `<f:image />`;
|
||||
try {
|
||||
transform(code);
|
||||
} catch (error) {
|
||||
_error = error;
|
||||
}
|
||||
expect(_error).toEqual(
|
||||
new SyntaxError(
|
||||
"undefined: Namespace tags are not supported by default. React's " +
|
||||
"JSX doesn't support namespace tags. You can turn on the " +
|
||||
"'throwIfNamespace' flag to bypass this warning." +
|
||||
'\n' +
|
||||
codeFrame.codeFrameColumns(
|
||||
code,
|
||||
{start: {line: 1, column: 2}},
|
||||
{highlightCode: true}
|
||||
)
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('should transform known hyphenated tags', () => {
|
||||
expect(transform(`<font-face />`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('wraps props in react spread for first spread attributes', () => {
|
||||
expect(transform(`<Component {...x} y={2} z />`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('wraps props in react spread for last spread attributes', () => {
|
||||
expect(transform(`<Component y={2} z { ... x } />`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('wraps props in react spread for middle spread attributes', () => {
|
||||
expect(transform(`<Component y={2} { ... x } z />`)).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('useBuiltIns false uses extend instead of Object.assign', () => {
|
||||
expect(
|
||||
transform(`<Component y={2} {...x} />`, {useBuiltIns: false})
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
@@ -11,15 +11,14 @@ const babel = require('@babel/core');
|
||||
const codeFrame = require('@babel/code-frame');
|
||||
const {wrap} = require('jest-snapshot-serializer-raw');
|
||||
|
||||
function transform(input, pluginOpts, babelOpts) {
|
||||
function transform(input, options) {
|
||||
return wrap(
|
||||
babel.transform(input, {
|
||||
configFile: false,
|
||||
sourceType: 'module',
|
||||
plugins: [
|
||||
'@babel/plugin-syntax-jsx',
|
||||
'@babel/plugin-transform-arrow-functions',
|
||||
...(pluginOpts && pluginOpts.development
|
||||
...(options && options.development
|
||||
? [
|
||||
'@babel/plugin-transform-react-jsx-source',
|
||||
'@babel/plugin-transform-react-jsx-self',
|
||||
@@ -30,380 +29,15 @@ function transform(input, pluginOpts, babelOpts) {
|
||||
{
|
||||
useBuiltIns: true,
|
||||
useCreateElement: false,
|
||||
...pluginOpts,
|
||||
...options,
|
||||
},
|
||||
],
|
||||
],
|
||||
...babelOpts,
|
||||
}).code
|
||||
);
|
||||
}
|
||||
|
||||
describe('transform react to jsx', () => {
|
||||
it('auto import pragma overrides regular pragma', () => {
|
||||
expect(
|
||||
transform(
|
||||
`/** @jsxAutoImport defaultExport */
|
||||
var x = <div><span /></div>
|
||||
`,
|
||||
{
|
||||
autoImport: 'namespace',
|
||||
importSource: 'foobar',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('import source pragma overrides regular pragma', () => {
|
||||
expect(
|
||||
transform(
|
||||
`/** @jsxImportSource baz */
|
||||
var x = <div><span /></div>
|
||||
`,
|
||||
{
|
||||
autoImport: 'namespace',
|
||||
importSource: 'foobar',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('multiple pragmas work', () => {
|
||||
expect(
|
||||
transform(
|
||||
`/** Some comment here
|
||||
* @jsxImportSource baz
|
||||
* @jsxAutoImport defaultExport
|
||||
*/
|
||||
var x = <div><span /></div>
|
||||
`,
|
||||
{
|
||||
autoImport: 'namespace',
|
||||
importSource: 'foobar',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('throws error when sourceType is module and autoImport is require', () => {
|
||||
const code = `var x = <div><span /></div>`;
|
||||
expect(() => {
|
||||
transform(code, {
|
||||
autoImport: 'require',
|
||||
});
|
||||
}).toThrow(
|
||||
'Babel `sourceType` must be set to `script` for autoImport ' +
|
||||
'to use `require` syntax. See Babel `sourceType` for details.\n' +
|
||||
codeFrame.codeFrameColumns(
|
||||
code,
|
||||
{start: {line: 1, column: 1}, end: {line: 1, column: 28}},
|
||||
{highlightCode: true}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('throws error when sourceType is script and autoImport is not require', () => {
|
||||
const code = `var x = <div><span /></div>`;
|
||||
expect(() => {
|
||||
transform(
|
||||
code,
|
||||
{
|
||||
autoImport: 'namespace',
|
||||
},
|
||||
{sourceType: 'script'}
|
||||
);
|
||||
}).toThrow(
|
||||
'Babel `sourceType` must be set to `module` for autoImport ' +
|
||||
'to use `namespace` syntax. See Babel `sourceType` for details.\n' +
|
||||
codeFrame.codeFrameColumns(
|
||||
code,
|
||||
{start: {line: 1, column: 1}, end: {line: 1, column: 28}},
|
||||
{highlightCode: true}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it("auto import that doesn't exist should throw error", () => {
|
||||
const code = `var x = <div><span /></div>`;
|
||||
expect(() => {
|
||||
transform(code, {
|
||||
autoImport: 'foo',
|
||||
});
|
||||
}).toThrow(
|
||||
'autoImport must be one of the following: none, require, namespace, defaultExport, namedExports\n' +
|
||||
codeFrame.codeFrameColumns(
|
||||
code,
|
||||
{start: {line: 1, column: 1}, end: {line: 1, column: 28}},
|
||||
{highlightCode: true}
|
||||
)
|
||||
);
|
||||
});
|
||||
|
||||
it('auto import can specify source', () => {
|
||||
expect(
|
||||
transform(`var x = <div><span /></div>`, {
|
||||
autoImport: 'namespace',
|
||||
importSource: 'foobar',
|
||||
})
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import require', () => {
|
||||
expect(
|
||||
transform(
|
||||
`var x = (
|
||||
<>
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
</>
|
||||
);`,
|
||||
{
|
||||
autoImport: 'require',
|
||||
},
|
||||
{
|
||||
sourceType: 'script',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import namespace', () => {
|
||||
expect(
|
||||
transform(
|
||||
`var x = (
|
||||
<>
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
</>
|
||||
);`,
|
||||
{
|
||||
autoImport: 'namespace',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import default', () => {
|
||||
expect(
|
||||
transform(
|
||||
`var x = (
|
||||
<>
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
</>
|
||||
);`,
|
||||
{
|
||||
autoImport: 'defaultExport',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import named exports', () => {
|
||||
expect(
|
||||
transform(
|
||||
`var x = (
|
||||
<>
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
</>
|
||||
);`,
|
||||
{
|
||||
autoImport: 'namedExports',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import with no JSX', () => {
|
||||
expect(
|
||||
transform(
|
||||
`var foo = "<div></div>"`,
|
||||
{
|
||||
autoImport: 'require',
|
||||
},
|
||||
{
|
||||
sourceType: 'script',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('complicated scope require', () => {
|
||||
expect(
|
||||
transform(
|
||||
`
|
||||
const Bar = () => {
|
||||
const Foo = () => {
|
||||
const Component = ({thing, ..._react}) => {
|
||||
if (!thing) {
|
||||
var _react2 = "something useless";
|
||||
var b = _react3();
|
||||
var c = _react5();
|
||||
var jsx = 1;
|
||||
var _jsx = 2;
|
||||
return <div />;
|
||||
};
|
||||
return <span />;
|
||||
};
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
autoImport: 'require',
|
||||
},
|
||||
{
|
||||
sourceType: 'script',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('complicated scope named exports', () => {
|
||||
expect(
|
||||
transform(
|
||||
`
|
||||
const Bar = () => {
|
||||
const Foo = () => {
|
||||
const Component = ({thing, ..._react}) => {
|
||||
if (!thing) {
|
||||
var _react2 = "something useless";
|
||||
var b = _react3();
|
||||
var jsx = 1;
|
||||
var _jsx = 2;
|
||||
return <div />;
|
||||
};
|
||||
return <span />;
|
||||
};
|
||||
}
|
||||
}
|
||||
`,
|
||||
{
|
||||
autoImport: 'namedExports',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import in dev', () => {
|
||||
expect(
|
||||
transform(
|
||||
`var x = (
|
||||
<>
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
</>
|
||||
);`,
|
||||
{
|
||||
autoImport: 'namedExports',
|
||||
development: true,
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import none', () => {
|
||||
expect(
|
||||
transform(
|
||||
`var x = (
|
||||
<>
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
</>
|
||||
);`,
|
||||
{
|
||||
autoImport: 'none',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import undefined', () => {
|
||||
expect(
|
||||
transform(
|
||||
`var x = (
|
||||
<>
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
</>
|
||||
);`
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import with namespaces already defined', () => {
|
||||
expect(
|
||||
transform(
|
||||
`
|
||||
import * as _react from "foo";
|
||||
const react = _react(1);
|
||||
const _react1 = react;
|
||||
const _react2 = react;
|
||||
var x = (
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
);`,
|
||||
{
|
||||
autoImport: 'namespace',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('auto import with react already defined', () => {
|
||||
expect(
|
||||
transform(
|
||||
`
|
||||
import * as react from "react";
|
||||
var y = react.createElement("div", {foo: 1});
|
||||
var x = (
|
||||
<div>
|
||||
<div key="1" />
|
||||
<div key="2" meow="wolf" />
|
||||
<div key="3" />
|
||||
<div {...props} key="4" />
|
||||
</div>
|
||||
);`,
|
||||
|
||||
{
|
||||
autoImport: 'namespace',
|
||||
}
|
||||
)
|
||||
).toMatchSnapshot();
|
||||
});
|
||||
|
||||
it('fragment with no children', () => {
|
||||
expect(transform(`var x = <></>`)).toMatchSnapshot();
|
||||
});
|
||||
@@ -658,11 +292,11 @@ describe('transform react to jsx', () => {
|
||||
}
|
||||
expect(_error).toEqual(
|
||||
new SyntaxError(
|
||||
'unknown: Spread children are not supported in React.' +
|
||||
'undefined: Spread children are not supported in React.' +
|
||||
'\n' +
|
||||
codeFrame.codeFrameColumns(
|
||||
code,
|
||||
{start: {line: 1, column: 6}, end: {line: 1, column: 19}},
|
||||
{start: {line: 1, column: 6}},
|
||||
{highlightCode: true}
|
||||
)
|
||||
)
|
||||
@@ -812,13 +446,13 @@ describe('transform react to jsx', () => {
|
||||
}
|
||||
expect(_error).toEqual(
|
||||
new SyntaxError(
|
||||
"unknown: Namespace tags are not supported by default. React's " +
|
||||
"undefined: Namespace tags are not supported by default. React's " +
|
||||
"JSX doesn't support namespace tags. You can turn on the " +
|
||||
"'throwIfNamespace' flag to bypass this warning." +
|
||||
'\n' +
|
||||
codeFrame.codeFrameColumns(
|
||||
code,
|
||||
{start: {line: 1, column: 2}, end: {line: 1, column: 9}},
|
||||
{start: {line: 1, column: 2}},
|
||||
{highlightCode: true}
|
||||
)
|
||||
)
|
||||
|
||||
@@ -0,0 +1,213 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`transform react to jsx React.Fragment to set keys and source 1`] = `
|
||||
var _jsxFileName = "";
|
||||
var x = React.createElement(React.Fragment, {
|
||||
key: "foo",
|
||||
__source: {
|
||||
fileName: _jsxFileName,
|
||||
lineNumber: 1
|
||||
},
|
||||
__self: this
|
||||
}, React.createElement("div", {
|
||||
__source: {
|
||||
fileName: _jsxFileName,
|
||||
lineNumber: 1
|
||||
},
|
||||
__self: this
|
||||
}));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx adds appropriate new lines when using spread attribute 1`] = `
|
||||
React.createElement(Component, Object.assign({}, props, {
|
||||
sound: "moo"
|
||||
}));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx arrow functions 1`] = `
|
||||
var foo = function () {
|
||||
var _this = this;
|
||||
|
||||
return function () {
|
||||
return React.createElement(_this, null);
|
||||
};
|
||||
};
|
||||
|
||||
var bar = function () {
|
||||
var _this2 = this;
|
||||
|
||||
return function () {
|
||||
return React.createElement(_this2.foo, null);
|
||||
};
|
||||
};
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx assignment 1`] = `
|
||||
var div = React.createElement(Component, Object.assign({}, props, {
|
||||
foo: "bar"
|
||||
}));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx concatenates adjacent string literals 1`] = `var x = React.createElement("div", null, "foo", "bar", "baz", React.createElement("div", null, "buz bang"), "qux", null, "quack");`;
|
||||
|
||||
exports[`transform react to jsx fragment with no children 1`] = `var x = React.createElement(React.Fragment, null);`;
|
||||
|
||||
exports[`transform react to jsx normal fragments not to set key and source 1`] = `
|
||||
var _jsxFileName = "";
|
||||
var x = React.createElement(React.Fragment, null, React.createElement("div", {
|
||||
__source: {
|
||||
fileName: _jsxFileName,
|
||||
lineNumber: 1
|
||||
},
|
||||
__self: this
|
||||
}));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should allow constructor as prop 1`] = `
|
||||
React.createElement(Component, {
|
||||
constructor: "foo"
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should allow deeper js namespacing 1`] = `React.createElement(Namespace.DeepNamespace.Component, null);`;
|
||||
|
||||
exports[`transform react to jsx should allow elements as attributes 1`] = `
|
||||
React.createElement("div", {
|
||||
attr: React.createElement("div", null)
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should allow js namespacing 1`] = `React.createElement(Namespace.Component, null);`;
|
||||
|
||||
exports[`transform react to jsx should allow nested fragments 1`] = `React.createElement("div", null, React.createElement(React.Fragment, null, React.createElement(React.Fragment, null, React.createElement("span", null, "Hello"), React.createElement("span", null, "world")), React.createElement(React.Fragment, null, React.createElement("span", null, "Goodbye"), React.createElement("span", null, "world"))));`;
|
||||
|
||||
exports[`transform react to jsx should avoid wrapping in extra parens if not needed 1`] = `
|
||||
var x = React.createElement("div", null, React.createElement(Component, null));
|
||||
var x = React.createElement("div", null, props.children);
|
||||
var x = React.createElement(Composite, null, props.children);
|
||||
var x = React.createElement(Composite, null, React.createElement(Composite2, null));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should convert simple tags 1`] = `var x = React.createElement("div", null);`;
|
||||
|
||||
exports[`transform react to jsx should convert simple text 1`] = `var x = React.createElement("div", null, "text");`;
|
||||
|
||||
exports[`transform react to jsx should escape xhtml jsxattribute 1`] = `
|
||||
React.createElement("div", {
|
||||
id: "w\\xF4w"
|
||||
});
|
||||
React.createElement("div", {
|
||||
id: "w"
|
||||
});
|
||||
React.createElement("div", {
|
||||
id: "w < w"
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should escape xhtml jsxtext 1`] = `
|
||||
React.createElement("div", null, "wow");
|
||||
React.createElement("div", null, "w\\xF4w");
|
||||
React.createElement("div", null, "w & w");
|
||||
React.createElement("div", null, "w & w");
|
||||
React.createElement("div", null, "w \\xA0 w");
|
||||
React.createElement("div", null, "this should not parse as unicode: \\xA0");
|
||||
React.createElement("div", null, "this should parse as nbsp: \\xA0 ");
|
||||
React.createElement("div", null, "this should parse as unicode: ", ' ');
|
||||
React.createElement("div", null, "w < w");
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should handle attributed elements 1`] = `
|
||||
var HelloMessage = React.createClass({
|
||||
render: function () {
|
||||
return React.createElement("div", null, "Hello ", this.props.name);
|
||||
}
|
||||
});
|
||||
React.render(React.createElement(HelloMessage, {
|
||||
name: React.createElement("span", null, "Sebastian")
|
||||
}), mountNode);
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should handle has own property correctly 1`] = `React.createElement("hasOwnProperty", null, "testing");`;
|
||||
|
||||
exports[`transform react to jsx should have correct comma in nested children 1`] = `var x = React.createElement("div", null, React.createElement("div", null, React.createElement("br", null)), React.createElement(Component, null, foo, React.createElement("br", null), bar), React.createElement("br", null));`;
|
||||
|
||||
exports[`transform react to jsx should insert commas after expressions before whitespace 1`] = `
|
||||
var x = React.createElement("div", {
|
||||
attr1: "foo" + "bar",
|
||||
attr2: "foo" + "bar" + "baz" + "bug",
|
||||
attr3: "foo" + "bar" + "baz" + "bug",
|
||||
attr4: "baz"
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should not add quotes to identifier names 1`] = `
|
||||
var e = React.createElement(F, {
|
||||
aaa: true,
|
||||
new: true,
|
||||
const: true,
|
||||
var: true,
|
||||
default: true,
|
||||
"foo-bar": true
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should not strip nbsp even couple with other whitespace 1`] = `React.createElement("div", null, "\\xA0 ");`;
|
||||
|
||||
exports[`transform react to jsx should not strip tags with a single child of nbsp 1`] = `React.createElement("div", null, "\\xA0");`;
|
||||
|
||||
exports[`transform react to jsx should properly handle comments adjacent to children 1`] = `var x = React.createElement("div", null, React.createElement("span", null), React.createElement("br", null));`;
|
||||
|
||||
exports[`transform react to jsx should properly handle comments between props 1`] = `
|
||||
var x = React.createElement("div", {
|
||||
/* a multi-line
|
||||
comment */
|
||||
attr1: "foo"
|
||||
}, React.createElement("span", {
|
||||
// a double-slash comment
|
||||
attr2: "bar"
|
||||
}));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should quote jsx attributes 1`] = `
|
||||
React.createElement("button", {
|
||||
"data-value": "a value"
|
||||
}, "Button");
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should support xml namespaces if flag 1`] = `
|
||||
React.createElement("f:image", {
|
||||
"n:attr": true
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx should transform known hyphenated tags 1`] = `React.createElement("font-face", null);`;
|
||||
|
||||
exports[`transform react to jsx useBuiltIns false uses extend instead of Object.assign 1`] = `
|
||||
function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); }
|
||||
|
||||
React.createElement(Component, _extends({
|
||||
y: 2
|
||||
}, x));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx wraps props in react spread for first spread attributes 1`] = `
|
||||
React.createElement(Component, Object.assign({}, x, {
|
||||
y: 2,
|
||||
z: true
|
||||
}));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx wraps props in react spread for last spread attributes 1`] = `
|
||||
React.createElement(Component, Object.assign({
|
||||
y: 2,
|
||||
z: true
|
||||
}, x));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx wraps props in react spread for middle spread attributes 1`] = `
|
||||
React.createElement(Component, Object.assign({
|
||||
y: 2
|
||||
}, x, {
|
||||
z: true
|
||||
}));
|
||||
`;
|
||||
@@ -38,230 +38,6 @@ var div = React.jsx(Component, Object.assign({}, props, {
|
||||
}));
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import can specify source 1`] = `
|
||||
import * as _foobar from "foobar";
|
||||
|
||||
var x = _foobar.jsx("div", {
|
||||
children: _foobar.jsx("span", {})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import default 1`] = `
|
||||
import _default from "react";
|
||||
|
||||
var x = _default.jsx(_default.Fragment, {
|
||||
children: _default.jsxs("div", {
|
||||
children: [_default.jsx("div", {}, "1"), _default.jsx("div", {
|
||||
meow: "wolf"
|
||||
}, "2"), _default.jsx("div", {}, "3"), _default.createElement("div", Object.assign({}, props, {
|
||||
key: "4"
|
||||
}))]
|
||||
})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import in dev 1`] = `
|
||||
import { createElement as _createElement } from "react";
|
||||
import { jsxDEV as _jsxDEV } from "react";
|
||||
import { Fragment as _Fragment } from "react";
|
||||
var _jsxFileName = "";
|
||||
|
||||
var x = _jsxDEV(_Fragment, {
|
||||
children: _jsxDEV("div", {
|
||||
children: [_jsxDEV("div", {}, "1", false, {
|
||||
fileName: _jsxFileName,
|
||||
lineNumber: 4
|
||||
}, this), _jsxDEV("div", {
|
||||
meow: "wolf"
|
||||
}, "2", false, {
|
||||
fileName: _jsxFileName,
|
||||
lineNumber: 5
|
||||
}, this), _jsxDEV("div", {}, "3", false, {
|
||||
fileName: _jsxFileName,
|
||||
lineNumber: 6
|
||||
}, this), _createElement("div", Object.assign({}, props, {
|
||||
key: "4",
|
||||
__source: {
|
||||
fileName: _jsxFileName,
|
||||
lineNumber: 7
|
||||
},
|
||||
__self: this
|
||||
}))]
|
||||
}, undefined, true, {
|
||||
fileName: _jsxFileName,
|
||||
lineNumber: 3
|
||||
}, this)
|
||||
}, undefined, false);
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import named exports 1`] = `
|
||||
import { createElement as _createElement } from "react";
|
||||
import { jsx as _jsx } from "react";
|
||||
import { jsxs as _jsxs } from "react";
|
||||
import { Fragment as _Fragment } from "react";
|
||||
|
||||
var x = _jsx(_Fragment, {
|
||||
children: _jsxs("div", {
|
||||
children: [_jsx("div", {}, "1"), _jsx("div", {
|
||||
meow: "wolf"
|
||||
}, "2"), _jsx("div", {}, "3"), _createElement("div", Object.assign({}, props, {
|
||||
key: "4"
|
||||
}))]
|
||||
})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import namespace 1`] = `
|
||||
import * as _react from "react";
|
||||
|
||||
var x = _react.jsx(_react.Fragment, {
|
||||
children: _react.jsxs("div", {
|
||||
children: [_react.jsx("div", {}, "1"), _react.jsx("div", {
|
||||
meow: "wolf"
|
||||
}, "2"), _react.jsx("div", {}, "3"), _react.createElement("div", Object.assign({}, props, {
|
||||
key: "4"
|
||||
}))]
|
||||
})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import none 1`] = `
|
||||
var x = React.jsx(React.Fragment, {
|
||||
children: React.jsxs("div", {
|
||||
children: [React.jsx("div", {}, "1"), React.jsx("div", {
|
||||
meow: "wolf"
|
||||
}, "2"), React.jsx("div", {}, "3"), React.createElement("div", Object.assign({}, props, {
|
||||
key: "4"
|
||||
}))]
|
||||
})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import pragma overrides regular pragma 1`] = `
|
||||
import _default from "foobar";
|
||||
|
||||
/** @jsxAutoImport defaultExport */
|
||||
var x = _default.jsx("div", {
|
||||
children: _default.jsx("span", {})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import require 1`] = `
|
||||
var _react = require("react");
|
||||
|
||||
var x = _react.jsx(_react.Fragment, {
|
||||
children: _react.jsxs("div", {
|
||||
children: [_react.jsx("div", {}, "1"), _react.jsx("div", {
|
||||
meow: "wolf"
|
||||
}, "2"), _react.jsx("div", {}, "3"), _react.createElement("div", Object.assign({}, props, {
|
||||
key: "4"
|
||||
}))]
|
||||
})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import undefined 1`] = `
|
||||
var x = React.jsx(React.Fragment, {
|
||||
children: React.jsxs("div", {
|
||||
children: [React.jsx("div", {}, "1"), React.jsx("div", {
|
||||
meow: "wolf"
|
||||
}, "2"), React.jsx("div", {}, "3"), React.createElement("div", Object.assign({}, props, {
|
||||
key: "4"
|
||||
}))]
|
||||
})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import with namespaces already defined 1`] = `
|
||||
import * as _react3 from "react";
|
||||
import * as _react from "foo";
|
||||
|
||||
const react = _react(1);
|
||||
|
||||
const _react1 = react;
|
||||
const _react2 = react;
|
||||
|
||||
var x = _react3.jsxs("div", {
|
||||
children: [_react3.jsx("div", {}, "1"), _react3.jsx("div", {
|
||||
meow: "wolf"
|
||||
}, "2"), _react3.jsx("div", {}, "3"), _react3.createElement("div", Object.assign({}, props, {
|
||||
key: "4"
|
||||
}))]
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx auto import with no JSX 1`] = `var foo = "<div></div>";`;
|
||||
|
||||
exports[`transform react to jsx auto import with react already defined 1`] = `
|
||||
import * as _react from "react";
|
||||
import * as react from "react";
|
||||
var y = react.createElement("div", {
|
||||
foo: 1
|
||||
});
|
||||
|
||||
var x = _react.jsxs("div", {
|
||||
children: [_react.jsx("div", {}, "1"), _react.jsx("div", {
|
||||
meow: "wolf"
|
||||
}, "2"), _react.jsx("div", {}, "3"), _react.createElement("div", Object.assign({}, props, {
|
||||
key: "4"
|
||||
}))]
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx complicated scope named exports 1`] = `
|
||||
import { jsx as _jsx2 } from "react";
|
||||
|
||||
const Bar = function () {
|
||||
const Foo = function () {
|
||||
const Component = function ({
|
||||
thing,
|
||||
..._react
|
||||
}) {
|
||||
if (!thing) {
|
||||
var _react2 = "something useless";
|
||||
|
||||
var b = _react3();
|
||||
|
||||
var jsx = 1;
|
||||
var _jsx = 2;
|
||||
return _jsx2("div", {});
|
||||
}
|
||||
|
||||
;
|
||||
return _jsx2("span", {});
|
||||
};
|
||||
};
|
||||
};
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx complicated scope require 1`] = `
|
||||
var _react4 = require("react");
|
||||
|
||||
const Bar = function () {
|
||||
const Foo = function () {
|
||||
const Component = function ({
|
||||
thing,
|
||||
..._react
|
||||
}) {
|
||||
if (!thing) {
|
||||
var _react2 = "something useless";
|
||||
|
||||
var b = _react3();
|
||||
|
||||
var c = _react5();
|
||||
|
||||
var jsx = 1;
|
||||
var _jsx = 2;
|
||||
return _react4.jsx("div", {});
|
||||
}
|
||||
|
||||
;
|
||||
return _react4.jsx("span", {});
|
||||
};
|
||||
};
|
||||
};
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx concatenates adjacent string literals 1`] = `
|
||||
var x = React.jsxs("div", {
|
||||
children: ["foo", "bar", "baz", React.jsx("div", {
|
||||
@@ -316,27 +92,6 @@ var x = React.jsxDEV(React.Fragment, {
|
||||
|
||||
exports[`transform react to jsx fragments to set keys 1`] = `var x = React.jsx(React.Fragment, {}, "foo");`;
|
||||
|
||||
exports[`transform react to jsx import source pragma overrides regular pragma 1`] = `
|
||||
import * as _baz from "baz";
|
||||
|
||||
/** @jsxImportSource baz */
|
||||
var x = _baz.jsx("div", {
|
||||
children: _baz.jsx("span", {})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx multiple pragmas work 1`] = `
|
||||
import _default from "baz";
|
||||
|
||||
/** Some comment here
|
||||
* @jsxImportSource baz
|
||||
* @jsxAutoImport defaultExport
|
||||
*/
|
||||
var x = _default.jsx("div", {
|
||||
children: _default.jsx("span", {})
|
||||
});
|
||||
`;
|
||||
|
||||
exports[`transform react to jsx nonStatic children 1`] = `
|
||||
var _jsxFileName = "";
|
||||
var x = React.jsxDEV("div", {
|
||||
|
||||
@@ -5,8 +5,8 @@
|
||||
"description": "@babel/plugin-transform-react-jsx",
|
||||
"main": "index.js",
|
||||
"dependencies": {
|
||||
"@babel/helper-module-imports": "^7.0.0",
|
||||
"esutils": "^2.0.0"
|
||||
|
||||
},
|
||||
"files": [
|
||||
"README.md",
|
||||
|
||||
@@ -24,50 +24,6 @@
|
||||
'use strict';
|
||||
|
||||
const esutils = require('esutils');
|
||||
const {
|
||||
isModule,
|
||||
addNamespace,
|
||||
addNamed,
|
||||
addDefault,
|
||||
} = require('@babel/helper-module-imports');
|
||||
|
||||
// These are all the valid auto import types (under the config autoImport)
|
||||
// that a user can specific
|
||||
const IMPORT_TYPES = {
|
||||
none: 'none', // default option. Will not import anything
|
||||
require: 'require', // var _react = require("react");
|
||||
namespace: 'namespace', // import * as _react from "react";
|
||||
defaultExport: 'defaultExport', // import _default from "react";
|
||||
namedExports: 'namedExports', // import { jsx } from "react";
|
||||
};
|
||||
|
||||
const JSX_AUTO_IMPORT_ANNOTATION_REGEX = /\*?\s*@jsxAutoImport\s+([^\s]+)/;
|
||||
const JSX_IMPORT_SOURCE_ANNOTATION_REGEX = /\*?\s*@jsxImportSource\s+([^\s]+)/;
|
||||
|
||||
// We want to use React.createElement, even in the case of
|
||||
// jsx, for <div {...props} key={key} /> to distinguish it
|
||||
// from <div key={key} {...props} />. This is an intermediary
|
||||
// step while we deprecate key spread from props. Afterwards,
|
||||
// we will remove createElement entirely
|
||||
function shouldUseCreateElement(path, types) {
|
||||
const openingPath = path.get('openingElement');
|
||||
const attributes = openingPath.node.attributes;
|
||||
|
||||
let seenPropsSpread = false;
|
||||
for (let i = 0; i < attributes.length; i++) {
|
||||
const attr = attributes[i];
|
||||
if (
|
||||
seenPropsSpread &&
|
||||
types.isJSXAttribute(attr) &&
|
||||
attr.name.name === 'key'
|
||||
) {
|
||||
return true;
|
||||
} else if (types.isJSXSpreadAttribute(attr)) {
|
||||
seenPropsSpread = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function helper(babel, opts) {
|
||||
const {types: t} = babel;
|
||||
@@ -96,7 +52,7 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
|
||||
visitor.JSXElement = {
|
||||
exit(path, file) {
|
||||
let callExpr;
|
||||
if (shouldUseCreateElement(path, t)) {
|
||||
if (file.opts.useCreateElement || shouldUseCreateElement(path)) {
|
||||
callExpr = buildCreateElementCall(path, file);
|
||||
} else {
|
||||
callExpr = buildJSXElementCall(path, file);
|
||||
@@ -115,7 +71,12 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
|
||||
'Fragment tags are only supported in React 16 and up.',
|
||||
);
|
||||
}
|
||||
let callExpr = buildJSXFragmentCall(path, file);
|
||||
let callExpr;
|
||||
if (file.opts.useCreateElement) {
|
||||
callExpr = buildCreateElementFragmentCall(path, file);
|
||||
} else {
|
||||
callExpr = buildJSXFragmentCall(path, file);
|
||||
}
|
||||
|
||||
if (callExpr) {
|
||||
path.replaceWith(t.inherits(callExpr, path.node));
|
||||
@@ -186,6 +147,31 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
|
||||
return t.inherits(t.objectProperty(node.name, value), node);
|
||||
}
|
||||
|
||||
// We want to use React.createElement, even in the case of
|
||||
// jsx, for <div {...props} key={key} /> to distinguish it
|
||||
// from <div key={key} {...props} />. This is an intermediary
|
||||
// step while we deprecate key spread from props. Afterwards,
|
||||
// we will remove createElement entirely
|
||||
function shouldUseCreateElement(path) {
|
||||
const openingPath = path.get('openingElement');
|
||||
const attributes = openingPath.node.attributes;
|
||||
|
||||
let seenPropsSpread = false;
|
||||
for (let i = 0; i < attributes.length; i++) {
|
||||
const attr = attributes[i];
|
||||
if (
|
||||
seenPropsSpread &&
|
||||
t.isJSXAttribute(attr) &&
|
||||
attr.name.name === 'key'
|
||||
) {
|
||||
return true;
|
||||
} else if (t.isJSXSpreadAttribute(attr)) {
|
||||
seenPropsSpread = true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// Builds JSX into:
|
||||
// Production: React.jsx(type, arguments, key)
|
||||
// Development: React.jsxDEV(type, arguments, key, isStaticChildren, source, self)
|
||||
@@ -276,7 +262,6 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
|
||||
if (opts.post) {
|
||||
opts.post(state, file);
|
||||
}
|
||||
|
||||
return (
|
||||
state.call ||
|
||||
t.callExpression(
|
||||
@@ -576,6 +561,38 @@ You can turn on the 'throwIfNamespace' flag to bypass this warning.`,
|
||||
|
||||
return attribs;
|
||||
}
|
||||
|
||||
function buildCreateElementFragmentCall(path, file) {
|
||||
if (opts.filter && !opts.filter(path.node, file)) {
|
||||
return;
|
||||
}
|
||||
|
||||
const openingPath = path.get('openingElement');
|
||||
openingPath.parent.children = t.react.buildChildren(openingPath.parent);
|
||||
|
||||
const args = [];
|
||||
const tagName = null;
|
||||
const tagExpr = file.get('jsxFragIdentifier')();
|
||||
|
||||
const state = {
|
||||
tagExpr: tagExpr,
|
||||
tagName: tagName,
|
||||
args: args,
|
||||
};
|
||||
|
||||
if (opts.pre) {
|
||||
opts.pre(state, file);
|
||||
}
|
||||
|
||||
// no attributes are allowed with <> syntax
|
||||
args.push(t.nullLiteral(), ...path.node.children);
|
||||
|
||||
if (opts.post) {
|
||||
opts.post(state, file);
|
||||
}
|
||||
|
||||
return state.call || t.callExpression(state.oldCallee, args);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = function(babel) {
|
||||
@@ -606,183 +623,25 @@ module.exports = function(babel) {
|
||||
},
|
||||
});
|
||||
|
||||
const createIdentifierName = (path, autoImport, name, importName) => {
|
||||
if (autoImport === IMPORT_TYPES.none) {
|
||||
return `React.${name}`;
|
||||
} else if (autoImport === IMPORT_TYPES.namedExports) {
|
||||
if (importName) {
|
||||
const identifierName = `${importName[name]}`;
|
||||
return identifierName;
|
||||
}
|
||||
} else {
|
||||
return `${importName}.${name}`;
|
||||
}
|
||||
};
|
||||
|
||||
function getImportNames(parentPath, state) {
|
||||
const imports = {};
|
||||
parentPath.traverse({
|
||||
JSXElement(path) {
|
||||
if (shouldUseCreateElement(path, t)) {
|
||||
imports.createElement = true;
|
||||
} else if (path.node.children.length > 1) {
|
||||
const importName = state.development ? 'jsxDEV' : 'jsxs';
|
||||
imports[importName] = true;
|
||||
} else {
|
||||
const importName = state.development ? 'jsxDEV' : 'jsx';
|
||||
imports[importName] = true;
|
||||
}
|
||||
},
|
||||
|
||||
JSXFragment(path) {
|
||||
imports.Fragment = true;
|
||||
},
|
||||
});
|
||||
return imports;
|
||||
}
|
||||
|
||||
function hasJSX(parentPath) {
|
||||
let fileHasJSX = false;
|
||||
parentPath.traverse({
|
||||
JSXElement(path) {
|
||||
fileHasJSX = true;
|
||||
path.stop();
|
||||
},
|
||||
|
||||
JSXFragment(path) {
|
||||
fileHasJSX = true;
|
||||
path.stop();
|
||||
},
|
||||
});
|
||||
|
||||
return fileHasJSX;
|
||||
}
|
||||
|
||||
function addAutoImports(path, state) {
|
||||
if (state.autoImport === IMPORT_TYPES.none) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (IMPORT_TYPES[state.autoImport] === undefined) {
|
||||
throw path.buildCodeFrameError(
|
||||
'autoImport must be one of the following: ' +
|
||||
Object.keys(IMPORT_TYPES).join(', '),
|
||||
);
|
||||
}
|
||||
if (state.autoImport === IMPORT_TYPES.require && isModule(path)) {
|
||||
throw path.buildCodeFrameError(
|
||||
'Babel `sourceType` must be set to `script` for autoImport ' +
|
||||
'to use `require` syntax. See Babel `sourceType` for details.',
|
||||
);
|
||||
}
|
||||
if (state.autoImport !== IMPORT_TYPES.require && !isModule(path)) {
|
||||
throw path.buildCodeFrameError(
|
||||
'Babel `sourceType` must be set to `module` for autoImport to use `' +
|
||||
state.autoImport +
|
||||
'` syntax. See Babel `sourceType` for details.',
|
||||
);
|
||||
}
|
||||
|
||||
// import {jsx} from "react";
|
||||
// import {createElement} from "react";
|
||||
if (state.autoImport === IMPORT_TYPES.namedExports) {
|
||||
const imports = getImportNames(path, state);
|
||||
const importMap = {};
|
||||
|
||||
Object.keys(imports).forEach(importName => {
|
||||
importMap[importName] = addNamed(path, importName, state.source).name;
|
||||
});
|
||||
|
||||
return importMap;
|
||||
}
|
||||
|
||||
// add import to file and get the import name
|
||||
let name;
|
||||
if (state.autoImport === IMPORT_TYPES.require) {
|
||||
// var _react = require("react");
|
||||
name = addNamespace(path, state.source, {
|
||||
importedInterop: 'uncompiled',
|
||||
}).name;
|
||||
} else if (state.autoImport === IMPORT_TYPES.namespace) {
|
||||
// import * as _react from "react";
|
||||
name = addNamespace(path, state.source).name;
|
||||
} else if (state.autoImport === IMPORT_TYPES.defaultExport) {
|
||||
// import _default from "react";
|
||||
name = addDefault(path, state.source).name;
|
||||
}
|
||||
|
||||
return name;
|
||||
}
|
||||
|
||||
visitor.Program = {
|
||||
enter(path, state) {
|
||||
if (hasJSX(path)) {
|
||||
let autoImport = state.opts.autoImport || IMPORT_TYPES.none;
|
||||
let source = state.opts.importSource || 'react';
|
||||
const {file} = state;
|
||||
|
||||
if (file.ast.comments) {
|
||||
for (let i = 0; i < file.ast.comments.length; i++) {
|
||||
const comment = file.ast.comments[i];
|
||||
const jsxAutoImportMatches = JSX_AUTO_IMPORT_ANNOTATION_REGEX.exec(
|
||||
comment.value,
|
||||
);
|
||||
if (jsxAutoImportMatches) {
|
||||
autoImport = jsxAutoImportMatches[1];
|
||||
}
|
||||
const jsxImportSourceMatches = JSX_IMPORT_SOURCE_ANNOTATION_REGEX.exec(
|
||||
comment.value,
|
||||
);
|
||||
if (jsxImportSourceMatches) {
|
||||
source = jsxImportSourceMatches[1];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const importName = addAutoImports(path, {
|
||||
...state.opts,
|
||||
autoImport,
|
||||
source,
|
||||
});
|
||||
|
||||
state.set(
|
||||
'oldJSXIdentifier',
|
||||
createIdentifierParser(
|
||||
createIdentifierName(path, autoImport, 'createElement', importName),
|
||||
),
|
||||
);
|
||||
|
||||
state.set(
|
||||
'jsxIdentifier',
|
||||
createIdentifierParser(
|
||||
createIdentifierName(
|
||||
path,
|
||||
autoImport,
|
||||
state.opts.development ? 'jsxDEV' : 'jsx',
|
||||
importName,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
state.set(
|
||||
'jsxStaticIdentifier',
|
||||
createIdentifierParser(
|
||||
createIdentifierName(
|
||||
path,
|
||||
autoImport,
|
||||
state.opts.development ? 'jsxDEV' : 'jsxs',
|
||||
importName,
|
||||
),
|
||||
),
|
||||
);
|
||||
|
||||
state.set(
|
||||
'jsxFragIdentifier',
|
||||
createIdentifierParser(
|
||||
createIdentifierName(path, autoImport, 'Fragment', importName),
|
||||
),
|
||||
);
|
||||
}
|
||||
state.set(
|
||||
'oldJSXIdentifier',
|
||||
createIdentifierParser('React.createElement'),
|
||||
);
|
||||
state.set(
|
||||
'jsxIdentifier',
|
||||
createIdentifierParser(
|
||||
state.opts.development ? 'React.jsxDEV' : 'React.jsx',
|
||||
),
|
||||
);
|
||||
state.set(
|
||||
'jsxStaticIdentifier',
|
||||
createIdentifierParser(
|
||||
state.opts.development ? 'React.jsxDEV' : 'React.jsxs',
|
||||
),
|
||||
);
|
||||
state.set('jsxFragIdentifier', createIdentifierParser('React.Fragment'));
|
||||
},
|
||||
};
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
type Unsubscribe = () => void;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -405,18 +405,6 @@ const tests = {
|
||||
useHook();
|
||||
}
|
||||
`,
|
||||
`
|
||||
// Valid because the neither the condition nor the loop affect the hook call.
|
||||
function App(props) {
|
||||
const someObject = {propA: true};
|
||||
for (const propName in someObject) {
|
||||
if (propName === true) {
|
||||
} else {
|
||||
}
|
||||
}
|
||||
const [myState, setMyState] = useState(null);
|
||||
}
|
||||
`,
|
||||
],
|
||||
invalid: [
|
||||
{
|
||||
@@ -652,7 +640,14 @@ const tests = {
|
||||
}
|
||||
}
|
||||
`,
|
||||
errors: [loopError('useHook1'), loopError('useHook2', true)],
|
||||
errors: [
|
||||
loopError('useHook1'),
|
||||
|
||||
// NOTE: Small imprecision in error reporting due to caching means we
|
||||
// have a conditional error here instead of a loop error. However,
|
||||
// we will always get an error so this is acceptable.
|
||||
conditionalError('useHook2', true),
|
||||
],
|
||||
},
|
||||
{
|
||||
code: `
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "eslint-plugin-react-hooks",
|
||||
"description": "ESLint rules for React Hooks",
|
||||
"version": "2.4.0",
|
||||
"version": "2.3.0",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": "https://github.com/facebook/react.git",
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
|
||||
export default {
|
||||
meta: {
|
||||
fixable: 'code',
|
||||
schema: [
|
||||
{
|
||||
type: 'object',
|
||||
@@ -93,7 +94,7 @@ export default {
|
||||
reactiveHookName === 'useMemo' ||
|
||||
reactiveHookName === 'useCallback'
|
||||
) {
|
||||
// TODO: Can this have a suggestion?
|
||||
// TODO: Can this have an autofix?
|
||||
context.report({
|
||||
node: node.parent.callee,
|
||||
message:
|
||||
@@ -557,19 +558,12 @@ export default {
|
||||
`To fix this, pass [` +
|
||||
suggestedDependencies.join(', ') +
|
||||
`] as a second argument to the ${reactiveHookName} Hook.`,
|
||||
suggest: [
|
||||
{
|
||||
desc: `Add dependencies array: [${suggestedDependencies.join(
|
||||
', ',
|
||||
)}]`,
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(
|
||||
node,
|
||||
`, [${suggestedDependencies.join(', ')}]`,
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
fix(fixer) {
|
||||
return fixer.insertTextAfter(
|
||||
node,
|
||||
`, [${suggestedDependencies.join(', ')}]`,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
return;
|
||||
@@ -708,35 +702,27 @@ export default {
|
||||
` Move it inside the ${reactiveHookName} callback. ` +
|
||||
`Alternatively, wrap the '${fn.name.name}' definition into its own useCallback() Hook.`;
|
||||
}
|
||||
|
||||
let suggest;
|
||||
// Only handle the simple case: arrow functions.
|
||||
// Wrapping function declarations can mess up hoisting.
|
||||
if (suggestUseCallback && fn.type === 'Variable') {
|
||||
suggest = [
|
||||
{
|
||||
desc: `Wrap the '${fn.name.name}' definition into its own useCallback() Hook.`,
|
||||
fix(fixer) {
|
||||
return [
|
||||
// TODO: also add an import?
|
||||
fixer.insertTextBefore(fn.node.init, 'useCallback('),
|
||||
// TODO: ideally we'd gather deps here but it would require
|
||||
// restructuring the rule code. This will cause a new lint
|
||||
// error to appear immediately for useCallback. Note we're
|
||||
// not adding [] because would that changes semantics.
|
||||
fixer.insertTextAfter(fn.node.init, ')'),
|
||||
];
|
||||
},
|
||||
},
|
||||
];
|
||||
}
|
||||
// TODO: What if the function needs to change on every render anyway?
|
||||
// Should we suggest removing effect deps as an appropriate fix too?
|
||||
context.report({
|
||||
// TODO: Why not report this at the dependency site?
|
||||
node: fn.node,
|
||||
message,
|
||||
suggest,
|
||||
fix(fixer) {
|
||||
// Only handle the simple case: arrow functions.
|
||||
// Wrapping function declarations can mess up hoisting.
|
||||
if (suggestUseCallback && fn.type === 'Variable') {
|
||||
return [
|
||||
// TODO: also add an import?
|
||||
fixer.insertTextBefore(fn.node.init, 'useCallback('),
|
||||
// TODO: ideally we'd gather deps here but it would require
|
||||
// restructuring the rule code. This will cause a new lint
|
||||
// error to appear immediately for useCallback. Note we're
|
||||
// not adding [] because would that changes semantics.
|
||||
fixer.insertTextAfter(fn.node.init, ')'),
|
||||
];
|
||||
}
|
||||
},
|
||||
});
|
||||
});
|
||||
return;
|
||||
@@ -1022,20 +1008,13 @@ export default {
|
||||
'omit',
|
||||
)) +
|
||||
extraWarning,
|
||||
suggest: [
|
||||
{
|
||||
desc: `Update the dependencies array to be: [${suggestedDependencies.join(
|
||||
', ',
|
||||
)}]`,
|
||||
fix(fixer) {
|
||||
// TODO: consider preserving the comments or formatting?
|
||||
return fixer.replaceText(
|
||||
declaredDependenciesNode,
|
||||
`[${suggestedDependencies.join(', ')}]`,
|
||||
);
|
||||
},
|
||||
},
|
||||
],
|
||||
fix(fixer) {
|
||||
// TODO: consider preserving the comments or formatting?
|
||||
return fixer.replaceText(
|
||||
declaredDependenciesNode,
|
||||
`[${suggestedDependencies.join(', ')}]`,
|
||||
);
|
||||
},
|
||||
});
|
||||
}
|
||||
},
|
||||
|
||||
@@ -152,33 +152,31 @@ export default {
|
||||
* Populates `cyclic` with cyclic segments.
|
||||
*/
|
||||
|
||||
function countPathsFromStart(segment, pathHistory) {
|
||||
function countPathsFromStart(segment) {
|
||||
const {cache} = countPathsFromStart;
|
||||
let paths = cache.get(segment.id);
|
||||
const pathList = new Set(pathHistory);
|
||||
|
||||
// If `pathList` includes the current segment then we've found a cycle!
|
||||
// We need to fill `cyclic` with all segments inside cycle
|
||||
if (pathList.has(segment.id)) {
|
||||
const pathArray = [...pathList];
|
||||
const cyclicSegments = pathArray.slice(
|
||||
pathArray.indexOf(segment.id) + 1,
|
||||
);
|
||||
for (const cyclicSegment of cyclicSegments) {
|
||||
cyclic.add(cyclicSegment);
|
||||
// If `paths` is null then we've found a cycle! Add it to `cyclic` and
|
||||
// any other segments which are a part of this cycle.
|
||||
if (paths === null) {
|
||||
if (cyclic.has(segment.id)) {
|
||||
return 0;
|
||||
} else {
|
||||
cyclic.add(segment.id);
|
||||
for (const prevSegment of segment.prevSegments) {
|
||||
countPathsFromStart(prevSegment);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// add the current segment to pathList
|
||||
pathList.add(segment.id);
|
||||
|
||||
// We have a cached `paths`. Return it.
|
||||
if (paths !== undefined) {
|
||||
return paths;
|
||||
}
|
||||
|
||||
// Compute `paths` and cache it. Guarding against cycles.
|
||||
cache.set(segment.id, null);
|
||||
if (codePath.thrownSegments.includes(segment)) {
|
||||
paths = 0;
|
||||
} else if (segment.prevSegments.length === 0) {
|
||||
@@ -186,7 +184,7 @@ export default {
|
||||
} else {
|
||||
paths = 0;
|
||||
for (const prevSegment of segment.prevSegments) {
|
||||
paths += countPathsFromStart(prevSegment, pathList);
|
||||
paths += countPathsFromStart(prevSegment);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -223,33 +221,31 @@ export default {
|
||||
* Populates `cyclic` with cyclic segments.
|
||||
*/
|
||||
|
||||
function countPathsToEnd(segment, pathHistory) {
|
||||
function countPathsToEnd(segment) {
|
||||
const {cache} = countPathsToEnd;
|
||||
let paths = cache.get(segment.id);
|
||||
let pathList = new Set(pathHistory);
|
||||
|
||||
// If `pathList` includes the current segment then we've found a cycle!
|
||||
// We need to fill `cyclic` with all segments inside cycle
|
||||
if (pathList.has(segment.id)) {
|
||||
const pathArray = Array.from(pathList);
|
||||
const cyclicSegments = pathArray.slice(
|
||||
pathArray.indexOf(segment.id) + 1,
|
||||
);
|
||||
for (const cyclicSegment of cyclicSegments) {
|
||||
cyclic.add(cyclicSegment);
|
||||
// If `paths` is null then we've found a cycle! Add it to `cyclic` and
|
||||
// any other segments which are a part of this cycle.
|
||||
if (paths === null) {
|
||||
if (cyclic.has(segment.id)) {
|
||||
return 0;
|
||||
} else {
|
||||
cyclic.add(segment.id);
|
||||
for (const nextSegment of segment.nextSegments) {
|
||||
countPathsToEnd(nextSegment);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// add the current segment to pathList
|
||||
pathList.add(segment.id);
|
||||
|
||||
// We have a cached `paths`. Return it.
|
||||
if (paths !== undefined) {
|
||||
return paths;
|
||||
}
|
||||
|
||||
// Compute `paths` and cache it. Guarding against cycles.
|
||||
cache.set(segment.id, null);
|
||||
if (codePath.thrownSegments.includes(segment)) {
|
||||
paths = 0;
|
||||
} else if (segment.nextSegments.length === 0) {
|
||||
@@ -257,11 +253,11 @@ export default {
|
||||
} else {
|
||||
paths = 0;
|
||||
for (const nextSegment of segment.nextSegments) {
|
||||
paths += countPathsToEnd(nextSegment, pathList);
|
||||
paths += countPathsToEnd(nextSegment);
|
||||
}
|
||||
}
|
||||
|
||||
cache.set(segment.id, paths);
|
||||
|
||||
return paths;
|
||||
}
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"homepage": "https://reactjs.org/",
|
||||
"peerDependencies": {
|
||||
"jest": "^23.0.1 || ^24.0.0 || ^25.1.0",
|
||||
"jest": "^23.0.1 || ^24.0.0",
|
||||
"scheduler": "^0.15.0"
|
||||
},
|
||||
"files": [
|
||||
|
||||
@@ -19,7 +19,7 @@
|
||||
},
|
||||
"homepage": "https://reactjs.org/",
|
||||
"peerDependencies": {
|
||||
"jest": "^23.0.1 || ^24.0.0 || ^25.1.0",
|
||||
"jest": "^23.0.1 || ^24.0.0",
|
||||
"react": "^16.0.0",
|
||||
"react-test-renderer": "^16.0.0"
|
||||
},
|
||||
|
||||
175
packages/legacy-events/EventPluginHub.js
Normal file
175
packages/legacy-events/EventPluginHub.js
Normal file
@@ -0,0 +1,175 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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 invariant from 'shared/invariant';
|
||||
|
||||
import {
|
||||
injectEventPluginOrder,
|
||||
injectEventPluginsByName,
|
||||
plugins,
|
||||
} from './EventPluginRegistry';
|
||||
import {getFiberCurrentPropsFromNode} from './EventPluginUtils';
|
||||
import accumulateInto from './accumulateInto';
|
||||
import {runEventsInBatch} from './EventBatching';
|
||||
|
||||
import type {PluginModule} from './PluginModuleType';
|
||||
import type {ReactSyntheticEvent} from './ReactSyntheticEventType';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactFiber';
|
||||
import type {AnyNativeEvent} from './PluginModuleType';
|
||||
import type {TopLevelType} from './TopLevelEventTypes';
|
||||
import type {EventSystemFlags} from 'legacy-events/EventSystemFlags';
|
||||
|
||||
function isInteractive(tag) {
|
||||
return (
|
||||
tag === 'button' ||
|
||||
tag === 'input' ||
|
||||
tag === 'select' ||
|
||||
tag === 'textarea'
|
||||
);
|
||||
}
|
||||
|
||||
function shouldPreventMouseEvent(name, type, props) {
|
||||
switch (name) {
|
||||
case 'onClick':
|
||||
case 'onClickCapture':
|
||||
case 'onDoubleClick':
|
||||
case 'onDoubleClickCapture':
|
||||
case 'onMouseDown':
|
||||
case 'onMouseDownCapture':
|
||||
case 'onMouseMove':
|
||||
case 'onMouseMoveCapture':
|
||||
case 'onMouseUp':
|
||||
case 'onMouseUpCapture':
|
||||
return !!(props.disabled && isInteractive(type));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* This is a unified interface for event plugins to be installed and configured.
|
||||
*
|
||||
* Event plugins can implement the following properties:
|
||||
*
|
||||
* `extractEvents` {function(string, DOMEventTarget, string, object): *}
|
||||
* Required. When a top-level event is fired, this method is expected to
|
||||
* extract synthetic events that will in turn be queued and dispatched.
|
||||
*
|
||||
* `eventTypes` {object}
|
||||
* Optional, plugins that fire events must publish a mapping of registration
|
||||
* names that are used to register listeners. Values of this mapping must
|
||||
* be objects that contain `registrationName` or `phasedRegistrationNames`.
|
||||
*
|
||||
* `executeDispatch` {function(object, function, string)}
|
||||
* Optional, allows plugins to override how an event gets dispatched. By
|
||||
* default, the listener is simply invoked.
|
||||
*
|
||||
* Each plugin that is injected into `EventsPluginHub` is immediately operable.
|
||||
*
|
||||
* @public
|
||||
*/
|
||||
|
||||
/**
|
||||
* Methods for injecting dependencies.
|
||||
*/
|
||||
export const injection = {
|
||||
/**
|
||||
* @param {array} InjectedEventPluginOrder
|
||||
* @public
|
||||
*/
|
||||
injectEventPluginOrder,
|
||||
|
||||
/**
|
||||
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
|
||||
*/
|
||||
injectEventPluginsByName,
|
||||
};
|
||||
|
||||
/**
|
||||
* @param {object} inst The instance, which is the source of events.
|
||||
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
||||
* @return {?function} The stored callback.
|
||||
*/
|
||||
export function getListener(inst: Fiber, registrationName: string) {
|
||||
let listener;
|
||||
|
||||
// TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
|
||||
// live here; needs to be moved to a better place soon
|
||||
const stateNode = inst.stateNode;
|
||||
if (!stateNode) {
|
||||
// Work in progress (ex: onload events in incremental mode).
|
||||
return null;
|
||||
}
|
||||
const props = getFiberCurrentPropsFromNode(stateNode);
|
||||
if (!props) {
|
||||
// Work in progress.
|
||||
return null;
|
||||
}
|
||||
listener = props[registrationName];
|
||||
if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
|
||||
return null;
|
||||
}
|
||||
invariant(
|
||||
!listener || typeof listener === 'function',
|
||||
'Expected `%s` listener to be a function, instead got a value of `%s` type.',
|
||||
registrationName,
|
||||
typeof listener,
|
||||
);
|
||||
return listener;
|
||||
}
|
||||
|
||||
/**
|
||||
* Allows registered plugins an opportunity to extract events from top-level
|
||||
* native browser events.
|
||||
*
|
||||
* @return {*} An accumulation of synthetic events.
|
||||
* @internal
|
||||
*/
|
||||
function extractPluginEvents(
|
||||
topLevelType: TopLevelType,
|
||||
targetInst: null | Fiber,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
nativeEventTarget: null | EventTarget,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
): Array<ReactSyntheticEvent> | ReactSyntheticEvent | null {
|
||||
let events = null;
|
||||
for (let i = 0; i < plugins.length; i++) {
|
||||
// Not every plugin in the ordering may be loaded at runtime.
|
||||
const possiblePlugin: PluginModule<AnyNativeEvent> = plugins[i];
|
||||
if (possiblePlugin) {
|
||||
const extractedEvents = possiblePlugin.extractEvents(
|
||||
topLevelType,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
eventSystemFlags,
|
||||
);
|
||||
if (extractedEvents) {
|
||||
events = accumulateInto(events, extractedEvents);
|
||||
}
|
||||
}
|
||||
}
|
||||
return events;
|
||||
}
|
||||
|
||||
export function runExtractedPluginEventsInBatch(
|
||||
topLevelType: TopLevelType,
|
||||
targetInst: null | Fiber,
|
||||
nativeEvent: AnyNativeEvent,
|
||||
nativeEventTarget: null | EventTarget,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
) {
|
||||
const events = extractPluginEvents(
|
||||
topLevelType,
|
||||
targetInst,
|
||||
nativeEvent,
|
||||
nativeEventTarget,
|
||||
eventSystemFlags,
|
||||
);
|
||||
runEventsInBatch(events);
|
||||
}
|
||||
@@ -89,7 +89,7 @@ function publishEventForPlugin(
|
||||
): boolean {
|
||||
invariant(
|
||||
!eventNameDispatchConfigs.hasOwnProperty(eventName),
|
||||
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
|
||||
'EventPluginHub: More than one plugin attempted to publish the same ' +
|
||||
'event name, `%s`.',
|
||||
eventName,
|
||||
);
|
||||
@@ -133,7 +133,7 @@ function publishRegistrationName(
|
||||
): void {
|
||||
invariant(
|
||||
!registrationNameModules[registrationName],
|
||||
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
|
||||
'EventPluginHub: More than one plugin attempted to publish the same ' +
|
||||
'registration name, `%s`.',
|
||||
registrationName,
|
||||
);
|
||||
@@ -153,6 +153,8 @@ function publishRegistrationName(
|
||||
|
||||
/**
|
||||
* Registers plugins so that they can extract and dispatch events.
|
||||
*
|
||||
* @see {EventPluginHub}
|
||||
*/
|
||||
|
||||
/**
|
||||
@@ -191,6 +193,7 @@ export const possibleRegistrationNames = __DEV__ ? {} : (null: any);
|
||||
*
|
||||
* @param {array} InjectedEventPluginOrder
|
||||
* @internal
|
||||
* @see {EventPluginHub.injection.injectEventPluginOrder}
|
||||
*/
|
||||
export function injectEventPluginOrder(
|
||||
injectedEventPluginOrder: EventPluginOrder,
|
||||
@@ -206,13 +209,14 @@ export function injectEventPluginOrder(
|
||||
}
|
||||
|
||||
/**
|
||||
* Injects plugins to be used by plugin event system. The plugin names must be
|
||||
* Injects plugins to be used by `EventPluginHub`. The plugin names must be
|
||||
* in the ordering injected by `injectEventPluginOrder`.
|
||||
*
|
||||
* Plugins can be injected as part of page initialization or on-the-fly.
|
||||
*
|
||||
* @param {object} injectedNamesToPlugins Map from names to plugin modules.
|
||||
* @internal
|
||||
* @see {EventPluginHub.injection.injectEventPluginsByName}
|
||||
*/
|
||||
export function injectEventPluginsByName(
|
||||
injectedNamesToPlugins: NamesToPlugins,
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
traverseEnterLeave,
|
||||
} from 'shared/ReactTreeTraversal';
|
||||
|
||||
import getListener from 'legacy-events/getListener';
|
||||
import {getListener} from './EventPluginHub';
|
||||
import accumulateInto from './accumulateInto';
|
||||
import forEachAccumulated from './forEachAccumulated';
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ export type PluginModule<NativeEvent> = {
|
||||
nativeTarget: NativeEvent,
|
||||
nativeEventTarget: null | EventTarget,
|
||||
eventSystemFlags: EventSystemFlags,
|
||||
container?: Document | Element | Node,
|
||||
) => ?ReactSyntheticEvent,
|
||||
tapMoveThreshold?: number,
|
||||
...
|
||||
};
|
||||
|
||||
@@ -23,8 +23,8 @@ import {invokeGuardedCallbackAndCatchFirstError} from 'shared/ReactErrorUtils';
|
||||
let batchedUpdatesImpl = function(fn, bookkeeping) {
|
||||
return fn(bookkeeping);
|
||||
};
|
||||
let discreteUpdatesImpl = function(fn, a, b, c, d) {
|
||||
return fn(a, b, c, d);
|
||||
let discreteUpdatesImpl = function(fn, a, b, c) {
|
||||
return fn(a, b, c);
|
||||
};
|
||||
let flushDiscreteUpdatesImpl = function() {};
|
||||
let batchedEventUpdatesImpl = batchedUpdatesImpl;
|
||||
@@ -89,11 +89,11 @@ export function executeUserEventHandler(fn: any => void, value: any): void {
|
||||
}
|
||||
}
|
||||
|
||||
export function discreteUpdates(fn, a, b, c, d) {
|
||||
export function discreteUpdates(fn, a, b, c) {
|
||||
const prevIsInsideEventHandler = isInsideEventHandler;
|
||||
isInsideEventHandler = true;
|
||||
try {
|
||||
return discreteUpdatesImpl(fn, a, b, c, d);
|
||||
return discreteUpdatesImpl(fn, a, b, c);
|
||||
} finally {
|
||||
isInsideEventHandler = prevIsInsideEventHandler;
|
||||
if (!isInsideEventHandler) {
|
||||
|
||||
@@ -31,4 +31,4 @@ export type ReactSyntheticEvent = {|
|
||||
nativeEventTarget: EventTarget,
|
||||
) => ReactSyntheticEvent,
|
||||
isPersistent: () => boolean,
|
||||
|};
|
||||
|} & SyntheticEvent<>;
|
||||
|
||||
@@ -282,7 +282,7 @@ to return true:wantsResponderID| |
|
||||
+ + */
|
||||
|
||||
/**
|
||||
* A note about event ordering in the `EventPluginRegistry`.
|
||||
* A note about event ordering in the `EventPluginHub`.
|
||||
*
|
||||
* Suppose plugins are injected in the following order:
|
||||
*
|
||||
@@ -301,7 +301,7 @@ to return true:wantsResponderID| |
|
||||
* - When returned from `extractEvents`, deferred-dispatched events contain an
|
||||
* "accumulation" of deferred dispatches.
|
||||
* - These deferred dispatches are accumulated/collected before they are
|
||||
* returned, but processed at a later time by the `EventPluginRegistry` (hence the
|
||||
* returned, but processed at a later time by the `EventPluginHub` (hence the
|
||||
* name deferred).
|
||||
*
|
||||
* In the process of returning their deferred-dispatched events, event plugins
|
||||
@@ -325,9 +325,9 @@ to return true:wantsResponderID| |
|
||||
* - `R`s on-demand events (if any) (dispatched by `R` on-demand)
|
||||
* - `S`s on-demand events (if any) (dispatched by `S` on-demand)
|
||||
* - `C`s on-demand events (if any) (dispatched by `C` on-demand)
|
||||
* - `R`s extracted events (if any) (dispatched by `EventPluginRegistry`)
|
||||
* - `S`s extracted events (if any) (dispatched by `EventPluginRegistry`)
|
||||
* - `C`s extracted events (if any) (dispatched by `EventPluginRegistry`)
|
||||
* - `R`s extracted events (if any) (dispatched by `EventPluginHub`)
|
||||
* - `S`s extracted events (if any) (dispatched by `EventPluginHub`)
|
||||
* - `C`s extracted events (if any) (dispatched by `EventPluginHub`)
|
||||
*
|
||||
* In the case of `ResponderEventPlugin`: If the `startShouldSetResponder`
|
||||
* on-demand dispatch returns `true` (and some other details are satisfied) the
|
||||
@@ -336,9 +336,9 @@ to return true:wantsResponderID| |
|
||||
* will appear as follows:
|
||||
*
|
||||
* - `startShouldSetResponder` (`ResponderEventPlugin` dispatches on-demand)
|
||||
* - `touchStartCapture` (`EventPluginRegistry` dispatches as usual)
|
||||
* - `touchStart` (`EventPluginRegistry` dispatches as usual)
|
||||
* - `responderGrant/Reject` (`EventPluginRegistry` dispatches as usual)
|
||||
* - `touchStartCapture` (`EventPluginHub` dispatches as usual)
|
||||
* - `touchStart` (`EventPluginHub` dispatches as usual)
|
||||
* - `responderGrant/Reject` (`EventPluginHub` dispatches as usual)
|
||||
*/
|
||||
|
||||
function setResponderAndExtractTransfer(
|
||||
|
||||
@@ -211,7 +211,7 @@ describe('EventPluginRegistry', () => {
|
||||
expect(function() {
|
||||
EventPluginRegistry.injectEventPluginOrder(['one', 'two']);
|
||||
}).toThrowError(
|
||||
'EventPluginRegistry: More than one plugin attempted to publish the same ' +
|
||||
'EventPluginHub: More than one plugin attempted to publish the same ' +
|
||||
'registration name, `onPhotoCapture`.',
|
||||
);
|
||||
});
|
||||
|
||||
@@ -1,74 +0,0 @@
|
||||
/**
|
||||
* Copyright (c) Facebook, Inc. and its 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 {Fiber} from 'react-reconciler/src/ReactFiber';
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
import {getFiberCurrentPropsFromNode} from './EventPluginUtils';
|
||||
|
||||
function isInteractive(tag) {
|
||||
return (
|
||||
tag === 'button' ||
|
||||
tag === 'input' ||
|
||||
tag === 'select' ||
|
||||
tag === 'textarea'
|
||||
);
|
||||
}
|
||||
|
||||
function shouldPreventMouseEvent(name, type, props) {
|
||||
switch (name) {
|
||||
case 'onClick':
|
||||
case 'onClickCapture':
|
||||
case 'onDoubleClick':
|
||||
case 'onDoubleClickCapture':
|
||||
case 'onMouseDown':
|
||||
case 'onMouseDownCapture':
|
||||
case 'onMouseMove':
|
||||
case 'onMouseMoveCapture':
|
||||
case 'onMouseUp':
|
||||
case 'onMouseUpCapture':
|
||||
case 'onMouseEnter':
|
||||
return !!(props.disabled && isInteractive(type));
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param {object} inst The instance, which is the source of events.
|
||||
* @param {string} registrationName Name of listener (e.g. `onClick`).
|
||||
* @return {?function} The stored callback.
|
||||
*/
|
||||
export default function getListener(inst: Fiber, registrationName: string) {
|
||||
let listener;
|
||||
|
||||
// TODO: shouldPreventMouseEvent is DOM-specific and definitely should not
|
||||
// live here; needs to be moved to a better place soon
|
||||
const stateNode = inst.stateNode;
|
||||
if (!stateNode) {
|
||||
// Work in progress (ex: onload events in incremental mode).
|
||||
return null;
|
||||
}
|
||||
const props = getFiberCurrentPropsFromNode(stateNode);
|
||||
if (!props) {
|
||||
// Work in progress.
|
||||
return null;
|
||||
}
|
||||
listener = props[registrationName];
|
||||
if (shouldPreventMouseEvent(registrationName, inst.type, props)) {
|
||||
return null;
|
||||
}
|
||||
invariant(
|
||||
!listener || typeof listener === 'function',
|
||||
'Expected `%s` listener to be a function, instead got a value of `%s` type.',
|
||||
registrationName,
|
||||
typeof listener,
|
||||
);
|
||||
return listener;
|
||||
}
|
||||
2
packages/react-art/src/ReactART.js
vendored
2
packages/react-art/src/ReactART.js
vendored
@@ -5,7 +5,7 @@
|
||||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import ReactVersion from 'shared/ReactVersion';
|
||||
import {LegacyRoot} from 'shared/ReactRootTags';
|
||||
import {
|
||||
|
||||
2
packages/react-cache/src/LRU.js
vendored
2
packages/react-cache/src/LRU.js
vendored
@@ -77,7 +77,7 @@ export function createLRU<T>(limit: number) {
|
||||
}
|
||||
}
|
||||
|
||||
function add(value: Object, onDelete: () => mixed): Entry<Object> {
|
||||
function add(value: T, onDelete: () => mixed): Entry<T> {
|
||||
const entry = {
|
||||
value,
|
||||
onDelete,
|
||||
|
||||
2
packages/react-cache/src/ReactCache.js
vendored
2
packages/react-cache/src/ReactCache.js
vendored
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import {createLRU} from './LRU';
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ import {
|
||||
SimpleMemoComponent,
|
||||
ContextProvider,
|
||||
ForwardRef,
|
||||
Block,
|
||||
Chunk,
|
||||
} from 'shared/ReactWorkTags';
|
||||
|
||||
type CurrentDispatcherRef = typeof ReactSharedInternals.ReactCurrentDispatcher;
|
||||
@@ -117,8 +117,7 @@ function useState<S>(
|
||||
hook !== null
|
||||
? hook.memoizedState
|
||||
: typeof initialState === 'function'
|
||||
? // $FlowFixMe: Flow doesn't like mixed types
|
||||
initialState()
|
||||
? initialState()
|
||||
: initialState;
|
||||
hookLog.push({primitive: 'State', stackError: new Error(), value: state});
|
||||
return [state, (action: BasicStateAction<S>) => {}];
|
||||
@@ -628,7 +627,7 @@ export function inspectHooksOfFiber(
|
||||
fiber.tag !== FunctionComponent &&
|
||||
fiber.tag !== SimpleMemoComponent &&
|
||||
fiber.tag !== ForwardRef &&
|
||||
fiber.tag !== Block
|
||||
fiber.tag !== Chunk
|
||||
) {
|
||||
throw new Error(
|
||||
'Unknown Fiber. Needs to be a function component to inspect hooks.',
|
||||
|
||||
@@ -22,11 +22,6 @@ describe('ReactHooksInspection', () => {
|
||||
ReactDebugTools = require('react-debug-tools');
|
||||
});
|
||||
|
||||
if (!__EXPERIMENTAL__) {
|
||||
it("empty test so Jest doesn't complain", () => {});
|
||||
return;
|
||||
}
|
||||
|
||||
it('should inspect a simple useResponder hook', () => {
|
||||
const TestResponder = React.DEPRECATED_createResponder('TestResponder', {});
|
||||
|
||||
|
||||
6
packages/react-devtools-core/src/backend.js
vendored
6
packages/react-devtools-core/src/backend.js
vendored
@@ -32,7 +32,7 @@ type ConnectOptions = {
|
||||
|
||||
installHook(window);
|
||||
|
||||
const hook: ?DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
const hook: DevToolsHook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
|
||||
let savedComponentFilters: Array<ComponentFilter> = getDefaultComponentFilters();
|
||||
|
||||
@@ -48,10 +48,6 @@ function debug(methodName: string, ...args) {
|
||||
}
|
||||
|
||||
export function connectToDevTools(options: ?ConnectOptions) {
|
||||
if (hook == null) {
|
||||
// DevTools didn't get injected into this page (maybe b'c of the contentType).
|
||||
return;
|
||||
}
|
||||
const {
|
||||
host = 'localhost',
|
||||
nativeStyleEditorValidAttributes,
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
<ol>
|
||||
<li><a href="ReactDevTools.zip">download extension</a></li>
|
||||
<li>Double-click to extract</li>
|
||||
<li>Navigate to <code>edge://extensions/</code></li>
|
||||
<li>Enable "Developer mode"</li>
|
||||
<li>Click "LOAD UNPACKED"</li>
|
||||
<li>Select extracted extension folder (<code>ReactDevTools</code>)</li>
|
||||
</ol>
|
||||
@@ -1,12 +0,0 @@
|
||||
# The Microsoft Edge extension
|
||||
|
||||
The source code for this extension has moved to `shells/webextension`.
|
||||
|
||||
Modify the source code there and then rebuild this extension by running `node build` from this directory or `yarn run build:extension:edge` from the root directory.
|
||||
|
||||
## Testing in Microsoft Edge
|
||||
|
||||
You can test a local build of the web extension like so:
|
||||
|
||||
1. Build the extension: `node build`
|
||||
1. Follow the on-screen instructions.
|
||||
46
packages/react-devtools-extensions/edge/build.js
vendored
46
packages/react-devtools-extensions/edge/build.js
vendored
@@ -1,46 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const chalk = require('chalk');
|
||||
const {execSync} = require('child_process');
|
||||
const {join} = require('path');
|
||||
const {argv} = require('yargs');
|
||||
const build = require('../build');
|
||||
|
||||
const main = async () => {
|
||||
const {crx} = argv;
|
||||
|
||||
await build('edge');
|
||||
|
||||
const cwd = join(__dirname, 'build');
|
||||
if (crx) {
|
||||
const crxPath = join(
|
||||
__dirname,
|
||||
'..',
|
||||
'..',
|
||||
'..',
|
||||
'node_modules',
|
||||
'.bin',
|
||||
'crx'
|
||||
);
|
||||
|
||||
execSync(`${crxPath} pack ./unpacked -o ReactDevTools.crx`, {
|
||||
cwd,
|
||||
});
|
||||
}
|
||||
|
||||
console.log(chalk.green('\nThe Microsoft Edge extension has been built!'));
|
||||
|
||||
console.log(chalk.green('\nTo load this extension:'));
|
||||
console.log(chalk.yellow('Navigate to edge://extensions/'));
|
||||
console.log(chalk.yellow('Enable "Developer mode"'));
|
||||
console.log(chalk.yellow('Click "LOAD UNPACKED"'));
|
||||
console.log(chalk.yellow('Select extension folder - ' + cwd + '\\unpacked'));
|
||||
|
||||
console.log(chalk.green('\nYou can test this build by running:'));
|
||||
console.log(chalk.gray('\n# From the react-devtools root directory:'));
|
||||
console.log('yarn run test:edge\n');
|
||||
};
|
||||
|
||||
main();
|
||||
@@ -1,9 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const deploy = require('../deploy');
|
||||
|
||||
const main = async () => await deploy('edge');
|
||||
|
||||
main();
|
||||
@@ -1,52 +0,0 @@
|
||||
{
|
||||
"manifest_version": 2,
|
||||
"name": "React Developer Tools",
|
||||
"description": "Adds React debugging tools to the Microsoft Edge Developer Tools.",
|
||||
"version": "4.4.0",
|
||||
"version_name": "4.4.0",
|
||||
|
||||
"minimum_chrome_version": "49",
|
||||
|
||||
"icons": {
|
||||
"16": "icons/16-production.png",
|
||||
"32": "icons/32-production.png",
|
||||
"48": "icons/48-production.png",
|
||||
"128": "icons/128-production.png"
|
||||
},
|
||||
|
||||
"browser_action": {
|
||||
"default_icon": {
|
||||
"16": "icons/16-disabled.png",
|
||||
"32": "icons/32-disabled.png",
|
||||
"48": "icons/48-disabled.png",
|
||||
"128": "icons/128-disabled.png"
|
||||
},
|
||||
|
||||
"default_popup": "popups/disabled.html"
|
||||
},
|
||||
|
||||
"devtools_page": "main.html",
|
||||
|
||||
"content_security_policy": "script-src 'self' 'unsafe-eval'; object-src 'self'",
|
||||
"web_accessible_resources": [
|
||||
"main.html",
|
||||
"panel.html",
|
||||
"build/react_devtools_backend.js",
|
||||
"build/renderer.js"
|
||||
],
|
||||
|
||||
"background": {
|
||||
"scripts": ["build/background.js"],
|
||||
"persistent": false
|
||||
},
|
||||
|
||||
"permissions": ["file:///*", "http://*/*", "https://*/*"],
|
||||
|
||||
"content_scripts": [
|
||||
{
|
||||
"matches": ["<all_urls>"],
|
||||
"js": ["build/injectGlobalHook.js"],
|
||||
"run_at": "document_start"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
{
|
||||
"name": "react-devtools-experimental-edge",
|
||||
"alias": ["react-devtools-experimental-edge"],
|
||||
"files": ["index.html", "ReactDevTools.zip"]
|
||||
}
|
||||
29
packages/react-devtools-extensions/edge/test.js
vendored
29
packages/react-devtools-extensions/edge/test.js
vendored
@@ -1,29 +0,0 @@
|
||||
#!/usr/bin/env node
|
||||
|
||||
'use strict';
|
||||
|
||||
const open = require('open');
|
||||
const os = require('os');
|
||||
const osName = require('os-name');
|
||||
const START_URL = 'https://facebook.github.io/react/';
|
||||
const {resolve} = require('path');
|
||||
|
||||
const EXTENSION_PATH = resolve('./edge/build/unpacked');
|
||||
const extargs = `--load-extension=${EXTENSION_PATH}`;
|
||||
|
||||
const osname = osName(os.platform());
|
||||
let appname;
|
||||
|
||||
if (osname && osname.toLocaleLowerCase().startsWith('windows')) {
|
||||
appname = 'msedge';
|
||||
} else if (osname && osname.toLocaleLowerCase().startsWith('mac')) {
|
||||
appname = 'Microsoft Edge';
|
||||
} else if (osname && osname.toLocaleLowerCase().startsWith('linux')) {
|
||||
//Coming soon
|
||||
}
|
||||
|
||||
if (appname) {
|
||||
(async () => {
|
||||
await open(START_URL, {app: [appname, extargs]});
|
||||
})();
|
||||
}
|
||||
@@ -3,19 +3,15 @@
|
||||
"version": "0.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"build": "cross-env NODE_ENV=production yarn run build:chrome && yarn run build:firefox && yarn run build:edge",
|
||||
"build:dev": "cross-env NODE_ENV=development yarn run build:chrome && yarn run build:firefox && yarn run build:edge",
|
||||
"build": "cross-env NODE_ENV=production yarn run build:chrome && yarn run build:firefox",
|
||||
"build:dev": "cross-env NODE_ENV=development yarn run build:chrome && yarn run build:firefox",
|
||||
"build:chrome": "cross-env NODE_ENV=production node ./chrome/build",
|
||||
"build:chrome:crx": "cross-env NODE_ENV=production node ./chrome/build --crx",
|
||||
"build:chrome:dev": "cross-env NODE_ENV=development node ./chrome/build",
|
||||
"build:firefox": "cross-env NODE_ENV=production node ./firefox/build",
|
||||
"build:firefox:dev": "cross-env NODE_ENV=development node ./firefox/build",
|
||||
"build:edge": "cross-env NODE_ENV=production node ./edge/build",
|
||||
"build:edge:crx": "cross-env NODE_ENV=production node ./edge/build --crx",
|
||||
"build:edge:dev": "cross-env NODE_ENV=development node ./edge/build",
|
||||
"test:chrome": "node ./chrome/test",
|
||||
"test:firefox": "node ./firefox/test",
|
||||
"test:edge": "node ./edge/test"
|
||||
"test:firefox": "node ./firefox/test"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/core": "^7.1.6",
|
||||
@@ -36,8 +32,6 @@
|
||||
"firefox-profile": "^1.0.2",
|
||||
"node-libs-browser": "0.5.3",
|
||||
"nullthrows": "^1.0.0",
|
||||
"open": "^7.0.2",
|
||||
"os-name": "^3.1.0",
|
||||
"raw-loader": "^3.1.0",
|
||||
"style-loader": "^0.23.1",
|
||||
"web-ext": "^3.0.0",
|
||||
|
||||
@@ -22,10 +22,6 @@ function welcome(event) {
|
||||
window.addEventListener('message', welcome);
|
||||
|
||||
function setup(hook) {
|
||||
if (hook == null) {
|
||||
// DevTools didn't get injected into this page (maybe b'c of the contentType).
|
||||
return;
|
||||
}
|
||||
const Agent = require('react-devtools-shared/src/backend/agent').default;
|
||||
const Bridge = require('react-devtools-shared/src/bridge').default;
|
||||
const {initBackend} = require('react-devtools-shared/src/backend');
|
||||
|
||||
@@ -88,7 +88,7 @@ if (sessionStorageGetItem(SESSION_STORAGE_RELOAD_AND_PROFILE_KEY) === 'true') {
|
||||
|
||||
// Inject a __REACT_DEVTOOLS_GLOBAL_HOOK__ global for React to interact with.
|
||||
// Only do this for HTML documents though, to avoid e.g. breaking syntax highlighting for XML docs.
|
||||
if ('text/html' === document.contentType) {
|
||||
if (document.contentType === 'text/html') {
|
||||
injectCode(
|
||||
';(' +
|
||||
installHook.toString() +
|
||||
|
||||
10
packages/react-devtools-extensions/src/main.js
vendored
10
packages/react-devtools-extensions/src/main.js
vendored
@@ -271,7 +271,7 @@ function createPanelIfReactLoaded() {
|
||||
// When the user chooses a different node in the browser Elements tab,
|
||||
// copy it over to the hook object so that we can sync the selection.
|
||||
chrome.devtools.inspectedWindow.eval(
|
||||
'(window.__REACT_DEVTOOLS_GLOBAL_HOOK__ && window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 !== $0) ?' +
|
||||
'(window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 !== $0) ?' +
|
||||
'(window.__REACT_DEVTOOLS_GLOBAL_HOOK__.$0 = $0, true) :' +
|
||||
'false',
|
||||
(didSelectionChange, evalError) => {
|
||||
@@ -355,9 +355,11 @@ function createPanelIfReactLoaded() {
|
||||
|
||||
// It's easiest to recreate the DevTools panel (to clean up potential stale state).
|
||||
// We can revisit this in the future as a small optimization.
|
||||
flushSync(() => root.unmount());
|
||||
|
||||
initBridgeAndStore();
|
||||
flushSync(() => {
|
||||
root.unmount(() => {
|
||||
initBridgeAndStore();
|
||||
});
|
||||
});
|
||||
});
|
||||
},
|
||||
);
|
||||
|
||||
21
packages/react-devtools-inline/src/backend.js
vendored
21
packages/react-devtools-inline/src/backend.js
vendored
@@ -73,18 +73,17 @@ function finishActivation(contentWindow: window) {
|
||||
const agent = new Agent(bridge);
|
||||
|
||||
const hook = contentWindow.__REACT_DEVTOOLS_GLOBAL_HOOK__;
|
||||
if (hook) {
|
||||
initBackend(hook, agent, contentWindow);
|
||||
|
||||
// Setup React Native style editor if a renderer like react-native-web has injected it.
|
||||
if (hook.resolveRNStyle) {
|
||||
setupNativeStyleEditor(
|
||||
bridge,
|
||||
agent,
|
||||
hook.resolveRNStyle,
|
||||
hook.nativeStyleEditorValidAttributes,
|
||||
);
|
||||
}
|
||||
initBackend(hook, agent, contentWindow);
|
||||
|
||||
// Setup React Native style editor if a renderer like react-native-web has injected it.
|
||||
if (hook.resolveRNStyle) {
|
||||
setupNativeStyleEditor(
|
||||
bridge,
|
||||
agent,
|
||||
hook.resolveRNStyle,
|
||||
hook.nativeStyleEditorValidAttributes,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
/** @flow */
|
||||
|
||||
import * as React from 'react';
|
||||
import {forwardRef} from 'react';
|
||||
import React, {forwardRef} from 'react';
|
||||
import Bridge from 'react-devtools-shared/src/bridge';
|
||||
import Store from 'react-devtools-shared/src/devtools/store';
|
||||
import DevTools from 'react-devtools-shared/src/devtools/views/DevTools';
|
||||
|
||||
@@ -15,7 +15,6 @@
|
||||
"local-storage-fallback": "^4.1.1",
|
||||
"lodash.throttle": "^4.1.1",
|
||||
"memoize-one": "^3.1.1",
|
||||
"react-virtualized-auto-sizer": "^1.0.2",
|
||||
"semver": "^6.3.0"
|
||||
"react-virtualized-auto-sizer": "^1.0.2"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1472,103 +1472,4 @@ describe('InspectedElementContext', () => {
|
||||
|
||||
done();
|
||||
});
|
||||
|
||||
it('should enable complex values to be copied to the clipboard', async done => {
|
||||
const Immutable = require('immutable');
|
||||
|
||||
const Example = () => null;
|
||||
|
||||
const set = new Set(['abc', 123]);
|
||||
const map = new Map([
|
||||
['name', 'Brian'],
|
||||
['food', 'sushi'],
|
||||
]);
|
||||
const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
|
||||
const mapOfMaps = new Map([
|
||||
['first', map],
|
||||
['second', map],
|
||||
]);
|
||||
const typedArray = Int8Array.from([100, -100, 0]);
|
||||
const arrayBuffer = typedArray.buffer;
|
||||
const dataView = new DataView(arrayBuffer);
|
||||
const immutable = Immutable.fromJS({
|
||||
a: [{hello: 'there'}, 'fixed', true],
|
||||
b: 123,
|
||||
c: {
|
||||
'1': 'xyz',
|
||||
xyz: 1,
|
||||
},
|
||||
});
|
||||
// $FlowFixMe
|
||||
const bigInt = BigInt(123); // eslint-disable-line no-undef
|
||||
|
||||
await utils.actAsync(() =>
|
||||
ReactDOM.render(
|
||||
<Example
|
||||
arrayBuffer={arrayBuffer}
|
||||
dataView={dataView}
|
||||
map={map}
|
||||
set={set}
|
||||
mapOfMaps={mapOfMaps}
|
||||
setOfSets={setOfSets}
|
||||
typedArray={typedArray}
|
||||
immutable={immutable}
|
||||
bigInt={bigInt}
|
||||
/>,
|
||||
document.createElement('div'),
|
||||
),
|
||||
);
|
||||
|
||||
const id = ((store.getElementIDAtIndex(0): any): number);
|
||||
|
||||
let copyPath: CopyInspectedElementPath = ((null: any): CopyInspectedElementPath);
|
||||
|
||||
function Suspender({target}) {
|
||||
const context = React.useContext(InspectedElementContext);
|
||||
copyPath = context.copyInspectedElementPath;
|
||||
return null;
|
||||
}
|
||||
|
||||
await utils.actAsync(
|
||||
() =>
|
||||
TestRenderer.create(
|
||||
<Contexts
|
||||
defaultSelectedElementID={id}
|
||||
defaultSelectedElementIndex={0}>
|
||||
<React.Suspense fallback={null}>
|
||||
<Suspender target={id} />
|
||||
</React.Suspense>
|
||||
</Contexts>,
|
||||
),
|
||||
false,
|
||||
);
|
||||
expect(copyPath).not.toBeNull();
|
||||
|
||||
// Should copy the whole value (not just the hydrated parts)
|
||||
copyPath(id, ['props']);
|
||||
jest.runOnlyPendingTimers();
|
||||
// Should not error despite lots of unserialized values.
|
||||
|
||||
global.mockClipboardCopy.mockReset();
|
||||
|
||||
// Should copy the nested property specified (not just the outer value)
|
||||
copyPath(id, ['props', 'bigInt']);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
|
||||
expect(global.mockClipboardCopy).toHaveBeenCalledWith(
|
||||
JSON.stringify('123n'),
|
||||
);
|
||||
|
||||
global.mockClipboardCopy.mockReset();
|
||||
|
||||
// Should copy the nested property specified (not just the outer value)
|
||||
copyPath(id, ['props', 'typedArray']);
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
|
||||
expect(global.mockClipboardCopy).toHaveBeenCalledWith(
|
||||
JSON.stringify({0: 100, 1: -100, 2: 0}),
|
||||
);
|
||||
|
||||
done();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -589,91 +589,4 @@ describe('InspectedElementContext', () => {
|
||||
JSON.stringify(nestedObject.a.b),
|
||||
);
|
||||
});
|
||||
|
||||
it('should enable complex values to be copied to the clipboard', () => {
|
||||
const Immutable = require('immutable');
|
||||
|
||||
const Example = () => null;
|
||||
|
||||
const set = new Set(['abc', 123]);
|
||||
const map = new Map([
|
||||
['name', 'Brian'],
|
||||
['food', 'sushi'],
|
||||
]);
|
||||
const setOfSets = new Set([new Set(['a', 'b', 'c']), new Set([1, 2, 3])]);
|
||||
const mapOfMaps = new Map([
|
||||
['first', map],
|
||||
['second', map],
|
||||
]);
|
||||
const typedArray = Int8Array.from([100, -100, 0]);
|
||||
const arrayBuffer = typedArray.buffer;
|
||||
const dataView = new DataView(arrayBuffer);
|
||||
const immutable = Immutable.fromJS({
|
||||
a: [{hello: 'there'}, 'fixed', true],
|
||||
b: 123,
|
||||
c: {
|
||||
'1': 'xyz',
|
||||
xyz: 1,
|
||||
},
|
||||
});
|
||||
// $FlowFixMe
|
||||
const bigInt = BigInt(123); // eslint-disable-line no-undef
|
||||
|
||||
act(() =>
|
||||
ReactDOM.render(
|
||||
<Example
|
||||
arrayBuffer={arrayBuffer}
|
||||
dataView={dataView}
|
||||
map={map}
|
||||
set={set}
|
||||
mapOfMaps={mapOfMaps}
|
||||
setOfSets={setOfSets}
|
||||
typedArray={typedArray}
|
||||
immutable={immutable}
|
||||
bigInt={bigInt}
|
||||
/>,
|
||||
document.createElement('div'),
|
||||
),
|
||||
);
|
||||
|
||||
const id = ((store.getElementIDAtIndex(0): any): number);
|
||||
const rendererID = ((store.getRendererIDForElement(id): any): number);
|
||||
|
||||
// Should copy the whole value (not just the hydrated parts)
|
||||
bridge.send('copyElementPath', {
|
||||
id,
|
||||
path: ['props'],
|
||||
rendererID,
|
||||
});
|
||||
jest.runOnlyPendingTimers();
|
||||
// Should not error despite lots of unserialized values.
|
||||
|
||||
global.mockClipboardCopy.mockReset();
|
||||
|
||||
// Should copy the nested property specified (not just the outer value)
|
||||
bridge.send('copyElementPath', {
|
||||
id,
|
||||
path: ['props', 'bigInt'],
|
||||
rendererID,
|
||||
});
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
|
||||
expect(global.mockClipboardCopy).toHaveBeenCalledWith(
|
||||
JSON.stringify('123n'),
|
||||
);
|
||||
|
||||
global.mockClipboardCopy.mockReset();
|
||||
|
||||
// Should copy the nested property specified (not just the outer value)
|
||||
bridge.send('copyElementPath', {
|
||||
id,
|
||||
path: ['props', 'typedArray'],
|
||||
rendererID,
|
||||
});
|
||||
jest.runOnlyPendingTimers();
|
||||
expect(global.mockClipboardCopy).toHaveBeenCalledTimes(1);
|
||||
expect(global.mockClipboardCopy).toHaveBeenCalledWith(
|
||||
JSON.stringify({0: 100, 1: -100, 2: 0}),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -19,10 +19,6 @@ export function initBackend(
|
||||
agent: Agent,
|
||||
global: Object,
|
||||
): () => void {
|
||||
if (hook == null) {
|
||||
// DevTools didn't get injected into this page (maybe b'c of the contentType).
|
||||
return () => {};
|
||||
}
|
||||
const subs = [
|
||||
hook.sub(
|
||||
'renderer-attached',
|
||||
|
||||
@@ -81,10 +81,6 @@ export function serializeToString(data: any): string {
|
||||
}
|
||||
cache.add(value);
|
||||
}
|
||||
// $FlowFixMe
|
||||
if (typeof value === 'bigint') {
|
||||
return value.toString() + 'n';
|
||||
}
|
||||
return value;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -7,8 +7,13 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useContext, useEffect, useLayoutEffect, useRef, useState} from 'react';
|
||||
import React, {
|
||||
useContext,
|
||||
useEffect,
|
||||
useLayoutEffect,
|
||||
useRef,
|
||||
useState,
|
||||
} from 'react';
|
||||
import {createPortal} from 'react-dom';
|
||||
import {RegistryContext} from './Contexts';
|
||||
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useContext} from 'react';
|
||||
import React, {useContext} from 'react';
|
||||
import {RegistryContext} from './Contexts';
|
||||
|
||||
import styles from './ContextMenuItem.css';
|
||||
|
||||
@@ -19,22 +19,18 @@ export default function useContextMenu({
|
||||
}: {|
|
||||
data: Object,
|
||||
id: string,
|
||||
ref: {current: ElementRef<'div'> | null},
|
||||
ref: ElementRef<HTMLElement>,
|
||||
|}) {
|
||||
const {showMenu} = useContext(RegistryContext);
|
||||
|
||||
useEffect(() => {
|
||||
if (ref.current !== null) {
|
||||
const handleContextMenu = (event: MouseEvent | TouchEvent) => {
|
||||
const handleContextMenu = event => {
|
||||
event.preventDefault();
|
||||
event.stopPropagation();
|
||||
|
||||
const pageX =
|
||||
event.pageX ||
|
||||
(event.touches && ((event: any): TouchEvent).touches[0].pageX);
|
||||
const pageY =
|
||||
event.pageY ||
|
||||
(event.touches && ((event: any): TouchEvent).touches[0].pageY);
|
||||
const pageX = event.pageX || (event.touches && event.touches[0].pageX);
|
||||
const pageY = event.pageY || (event.touches && event.touches[0].pageY);
|
||||
|
||||
showMenu({data, id, pageX, pageY});
|
||||
};
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {createContext} from 'react';
|
||||
import React, {createContext} from 'react';
|
||||
|
||||
// Cache implementation was forked from the React repo:
|
||||
// https://github.com/facebook/react/blob/master/packages/react-cache/src/ReactCache.js
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import Tooltip from '@reach/tooltip';
|
||||
|
||||
import styles from './Button.css';
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import styles from './ButtonIcon.css';
|
||||
|
||||
export type IconType =
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Fragment} from 'react';
|
||||
import React, {Fragment} from 'react';
|
||||
import {
|
||||
ElementTypeMemo,
|
||||
ElementTypeForwardRef,
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Suspense} from 'react';
|
||||
import React, {Suspense} from 'react';
|
||||
import Tree from './Tree';
|
||||
import SelectedElement from './SelectedElement';
|
||||
import {InspectedElementContextController} from './InspectedElementContext';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useCallback, useState} from 'react';
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import AutoSizeInput from './NativeStyleEditor/AutoSizeInput';
|
||||
import styles from './EditableName.css';
|
||||
|
||||
|
||||
@@ -7,8 +7,9 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Fragment, useRef} from 'react';
|
||||
import React, {Fragment, useRef} from 'react';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
import styles from './EditableValue.css';
|
||||
import {useEditableValue} from '../hooks';
|
||||
|
||||
@@ -50,7 +51,9 @@ export default function EditableValue({
|
||||
|
||||
switch (event.key) {
|
||||
case 'Enter':
|
||||
applyChanges();
|
||||
if (isValid && hasPendingChanges) {
|
||||
overrideValueFn(path, parsedValue);
|
||||
}
|
||||
break;
|
||||
case 'Escape':
|
||||
reset();
|
||||
@@ -60,12 +63,6 @@ export default function EditableValue({
|
||||
}
|
||||
};
|
||||
|
||||
const applyChanges = () => {
|
||||
if (isValid && hasPendingChanges) {
|
||||
overrideValueFn(path, parsedValue);
|
||||
}
|
||||
};
|
||||
|
||||
let placeholder = '';
|
||||
if (editableValue === undefined) {
|
||||
placeholder = '(undefined)';
|
||||
@@ -78,7 +75,6 @@ export default function EditableValue({
|
||||
<input
|
||||
autoComplete="new-password"
|
||||
className={`${isValid ? styles.Input : styles.Invalid} ${className}`}
|
||||
onBlur={applyChanges}
|
||||
onChange={handleChange}
|
||||
onKeyDown={handleKeyDown}
|
||||
placeholder={placeholder}
|
||||
@@ -86,6 +82,14 @@ export default function EditableValue({
|
||||
type="text"
|
||||
value={editableValue}
|
||||
/>
|
||||
{hasPendingChanges && (
|
||||
<Button
|
||||
className={styles.ResetButton}
|
||||
onClick={reset}
|
||||
title="Reset value">
|
||||
<ButtonIcon type="undo" />
|
||||
</Button>
|
||||
)}
|
||||
</Fragment>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Fragment, useContext, useMemo, useState} from 'react';
|
||||
import React, {Fragment, useContext, useMemo, useState} from 'react';
|
||||
import Store from 'react-devtools-shared/src/devtools/store';
|
||||
import Badge from './Badge';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import {
|
||||
ElementTypeForwardRef,
|
||||
ElementTypeMemo,
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
*/
|
||||
|
||||
import {copy} from 'clipboard-js';
|
||||
import * as React from 'react';
|
||||
import {useCallback, useContext, useRef, useState} from 'react';
|
||||
import React, {useCallback, useContext, useRef, useState} from 'react';
|
||||
import {BridgeContext, StoreContext} from '../context';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useCallback, useContext, useEffect, useState} from 'react';
|
||||
import React, {useCallback, useContext, useEffect, useState} from 'react';
|
||||
import {BridgeContext} from '../context';
|
||||
import Toggle from '../Toggle';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
*/
|
||||
|
||||
import {copy} from 'clipboard-js';
|
||||
import * as React from 'react';
|
||||
import {useCallback, useState} from 'react';
|
||||
import React, {useCallback, useState} from 'react';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
import KeyValue from './KeyValue';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useEffect, useRef, useState} from 'react';
|
||||
import React, {useEffect, useRef, useState} from 'react';
|
||||
import EditableValue from './EditableValue';
|
||||
import ExpandCollapseToggle from './ExpandCollapseToggle';
|
||||
import {alphaSortEntries, getMetaValueLabel} from '../utils';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Fragment, useLayoutEffect, useRef} from 'react';
|
||||
import React, {Fragment, useLayoutEffect, useRef} from 'react';
|
||||
import styles from './AutoSizeInput.css';
|
||||
|
||||
type Props = {
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import styles from './LayoutViewer.css';
|
||||
|
||||
import type {Layout} from './types';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useContext, useMemo, useRef, useState} from 'react';
|
||||
import React, {useContext, useMemo, useRef, useState} from 'react';
|
||||
import {unstable_batchedUpdates as batchedUpdates} from 'react-dom';
|
||||
import {copy} from 'clipboard-js';
|
||||
import {
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Fragment, useContext, useMemo} from 'react';
|
||||
import React, {Fragment, useContext, useMemo} from 'react';
|
||||
import {StoreContext} from 'react-devtools-shared/src/devtools/views/context';
|
||||
import {useSubscription} from 'react-devtools-shared/src/devtools/views/hooks';
|
||||
import {NativeStyleContext} from './context';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {createContext, useCallback, useContext, useEffect} from 'react';
|
||||
import React, {createContext, useCallback, useContext, useEffect} from 'react';
|
||||
import {createResource} from '../../cache';
|
||||
import {BridgeContext, StoreContext} from '../context';
|
||||
import {TreeStateContext} from './TreeContext';
|
||||
|
||||
@@ -6,8 +6,7 @@
|
||||
*
|
||||
* @flow
|
||||
*/
|
||||
import * as React from 'react';
|
||||
import {
|
||||
import React, {
|
||||
Fragment,
|
||||
useCallback,
|
||||
useContext,
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useCallback, useContext, useEffect, useRef} from 'react';
|
||||
import React, {useCallback, useContext, useEffect, useRef} from 'react';
|
||||
import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
|
||||
@@ -8,8 +8,7 @@
|
||||
*/
|
||||
|
||||
import {copy} from 'clipboard-js';
|
||||
import * as React from 'react';
|
||||
import {Fragment, useCallback, useContext} from 'react';
|
||||
import React, {Fragment, useCallback, useContext} from 'react';
|
||||
import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
|
||||
import {BridgeContext, ContextMenuContext, StoreContext} from '../context';
|
||||
import ContextMenu from '../../ContextMenu/ContextMenu';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useContext, useMemo} from 'react';
|
||||
import React, {useContext, useMemo} from 'react';
|
||||
import {TreeStateContext} from './TreeContext';
|
||||
import {SettingsContext} from '../Settings/SettingsContext';
|
||||
import TreeFocusedContext from './TreeFocusedContext';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
import React, {
|
||||
Fragment,
|
||||
Suspense,
|
||||
useCallback,
|
||||
|
||||
@@ -24,8 +24,7 @@
|
||||
// For this reason, changes to the tree context are processed in sequence: tree -> search -> owners
|
||||
// This enables each section to potentially override (or mask) previous values.
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
|
||||
@@ -12,8 +12,7 @@
|
||||
import '@reach/menu-button/styles.css';
|
||||
import '@reach/tooltip/styles.css';
|
||||
|
||||
import * as React from 'react';
|
||||
import {useEffect, useMemo, useState} from 'react';
|
||||
import React, {useEffect, useMemo, useState} from 'react';
|
||||
import Store from '../store';
|
||||
import {BridgeContext, ContextMenuContext, StoreContext} from './context';
|
||||
import Components from './Components/Components';
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Component} from 'react';
|
||||
import React, {Component} from 'react';
|
||||
import styles from './ErrorBoundary.css';
|
||||
|
||||
type Props = {|
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
import styles from './Icon.css';
|
||||
|
||||
export type IconType =
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {
|
||||
import React, {
|
||||
createContext,
|
||||
useCallback,
|
||||
useContext,
|
||||
|
||||
@@ -7,7 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import React from 'react';
|
||||
|
||||
import styles from './ChartNode.css';
|
||||
|
||||
@@ -18,8 +18,6 @@ type Props = {|
|
||||
label: string,
|
||||
onClick: (event: SyntheticMouseEvent<*>) => mixed,
|
||||
onDoubleClick?: (event: SyntheticMouseEvent<*>) => mixed,
|
||||
onMouseEnter: (event: SyntheticMouseEvent<*>) => mixed,
|
||||
onMouseLeave: (event: SyntheticMouseEvent<*>) => mixed,
|
||||
placeLabelAboveNode?: boolean,
|
||||
textStyle?: Object,
|
||||
width: number,
|
||||
@@ -35,8 +33,6 @@ export default function ChartNode({
|
||||
isDimmed = false,
|
||||
label,
|
||||
onClick,
|
||||
onMouseEnter,
|
||||
onMouseLeave,
|
||||
onDoubleClick,
|
||||
textStyle,
|
||||
width,
|
||||
@@ -45,13 +41,12 @@ export default function ChartNode({
|
||||
}: Props) {
|
||||
return (
|
||||
<g className={styles.Group} transform={`translate(${x},${y})`}>
|
||||
<title>{label}</title>
|
||||
<rect
|
||||
width={width}
|
||||
height={height}
|
||||
fill={color}
|
||||
onClick={onClick}
|
||||
onMouseEnter={onMouseEnter}
|
||||
onMouseLeave={onMouseLeave}
|
||||
onDoubleClick={onDoubleClick}
|
||||
className={styles.Rect}
|
||||
style={{
|
||||
|
||||
@@ -7,8 +7,7 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {useCallback, useContext} from 'react';
|
||||
import React, {useCallback, useContext} from 'react';
|
||||
import {ProfilerContext} from './ProfilerContext';
|
||||
import Button from '../Button';
|
||||
import ButtonIcon from '../ButtonIcon';
|
||||
|
||||
@@ -7,28 +7,23 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {forwardRef, useCallback, useContext, useMemo, useState} from 'react';
|
||||
import React, {forwardRef, useCallback, useContext, useMemo} from 'react';
|
||||
import AutoSizer from 'react-virtualized-auto-sizer';
|
||||
import {FixedSizeList} from 'react-window';
|
||||
import {ProfilerContext} from './ProfilerContext';
|
||||
import NoCommitData from './NoCommitData';
|
||||
import CommitFlamegraphListItem from './CommitFlamegraphListItem';
|
||||
import HoveredFiberInfo from './HoveredFiberInfo';
|
||||
import {scale} from './utils';
|
||||
import {StoreContext} from '../context';
|
||||
import {SettingsContext} from '../Settings/SettingsContext';
|
||||
import Tooltip from './Tooltip';
|
||||
|
||||
import styles from './CommitFlamegraph.css';
|
||||
|
||||
import type {TooltipFiberData} from './HoveredFiberInfo';
|
||||
import type {ChartData, ChartNode} from './FlamegraphChartBuilder';
|
||||
import type {CommitTree} from './types';
|
||||
|
||||
export type ItemData = {|
|
||||
chartData: ChartData,
|
||||
hoverFiber: (fiberData: TooltipFiberData | null) => void,
|
||||
scaleX: (value: number, fallbackValue: number) => number,
|
||||
selectedChartNode: ChartNode | null,
|
||||
selectedChartNodeIndex: number,
|
||||
@@ -96,7 +91,6 @@ type Props = {|
|
||||
|};
|
||||
|
||||
function CommitFlamegraph({chartData, commitTree, height, width}: Props) {
|
||||
const [hoveredFiberData, hoverFiber] = useState<number | null>(null);
|
||||
const {lineHeight} = useContext(SettingsContext);
|
||||
const {selectFiber, selectedFiberID} = useContext(ProfilerContext);
|
||||
|
||||
@@ -124,7 +118,6 @@ function CommitFlamegraph({chartData, commitTree, height, width}: Props) {
|
||||
const itemData = useMemo<ItemData>(
|
||||
() => ({
|
||||
chartData,
|
||||
hoverFiber,
|
||||
scaleX: scale(
|
||||
0,
|
||||
selectedChartNode !== null
|
||||
@@ -138,37 +131,19 @@ function CommitFlamegraph({chartData, commitTree, height, width}: Props) {
|
||||
selectFiber,
|
||||
width,
|
||||
}),
|
||||
[
|
||||
chartData,
|
||||
hoverFiber,
|
||||
selectedChartNode,
|
||||
selectedChartNodeIndex,
|
||||
selectFiber,
|
||||
width,
|
||||
],
|
||||
);
|
||||
|
||||
// Tooltip used to show summary of fiber info on hover
|
||||
const tooltipLabel = useMemo(
|
||||
() =>
|
||||
hoveredFiberData !== null ? (
|
||||
<HoveredFiberInfo fiberData={hoveredFiberData} />
|
||||
) : null,
|
||||
[hoveredFiberData],
|
||||
[chartData, selectedChartNode, selectedChartNodeIndex, selectFiber, width],
|
||||
);
|
||||
|
||||
return (
|
||||
<Tooltip label={tooltipLabel}>
|
||||
<FixedSizeList
|
||||
height={height}
|
||||
innerElementType={InnerElementType}
|
||||
itemCount={chartData.depth}
|
||||
itemData={itemData}
|
||||
itemSize={lineHeight}
|
||||
width={width}>
|
||||
{CommitFlamegraphListItem}
|
||||
</FixedSizeList>
|
||||
</Tooltip>
|
||||
<FixedSizeList
|
||||
height={height}
|
||||
innerElementType={InnerElementType}
|
||||
itemCount={chartData.depth}
|
||||
itemData={itemData}
|
||||
itemSize={lineHeight}
|
||||
width={width}>
|
||||
{CommitFlamegraphListItem}
|
||||
</FixedSizeList>
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -7,15 +7,13 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import * as React from 'react';
|
||||
import {Fragment, memo, useCallback, useContext} from 'react';
|
||||
import React, {Fragment, memo, useCallback, useContext} from 'react';
|
||||
import {areEqual} from 'react-window';
|
||||
import {barWidthThreshold} from './constants';
|
||||
import {getGradientColor} from './utils';
|
||||
import ChartNode from './ChartNode';
|
||||
import {SettingsContext} from '../Settings/SettingsContext';
|
||||
|
||||
import type {ChartNode as ChartNodeType} from './FlamegraphChartBuilder';
|
||||
import type {ItemData} from './CommitFlamegraph';
|
||||
|
||||
type Props = {
|
||||
@@ -28,7 +26,6 @@ type Props = {
|
||||
function CommitFlamegraphListItem({data, index, style}: Props) {
|
||||
const {
|
||||
chartData,
|
||||
hoverFiber,
|
||||
scaleX,
|
||||
selectedChartNode,
|
||||
selectedChartNodeIndex,
|
||||
@@ -38,7 +35,6 @@ function CommitFlamegraphListItem({data, index, style}: Props) {
|
||||
const {renderPathNodes, maxSelfDuration, rows} = chartData;
|
||||
|
||||
const {lineHeight} = useContext(SettingsContext);
|
||||
|
||||
const handleClick = useCallback(
|
||||
(event: SyntheticMouseEvent<*>, id: number, name: string) => {
|
||||
event.stopPropagation();
|
||||
@@ -47,15 +43,6 @@ function CommitFlamegraphListItem({data, index, style}: Props) {
|
||||
[selectFiber],
|
||||
);
|
||||
|
||||
const handleMouseEnter = (nodeData: ChartNodeType) => {
|
||||
const {id, name} = nodeData;
|
||||
hoverFiber({id, name});
|
||||
};
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
hoverFiber(null);
|
||||
};
|
||||
|
||||
// List items are absolutely positioned using the CSS "top" attribute.
|
||||
// The "left" value will always be 0.
|
||||
// Since height is fixed, and width is based on the node's duration,
|
||||
@@ -117,8 +104,6 @@ function CommitFlamegraphListItem({data, index, style}: Props) {
|
||||
key={id}
|
||||
label={label}
|
||||
onClick={event => handleClick(event, id, name)}
|
||||
onMouseEnter={() => handleMouseEnter(chartNode)}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
textStyle={{color: textColor}}
|
||||
width={nodeWidth}
|
||||
x={nodeOffset - selectedNodeOffset}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user