Skip to content

Commit

Permalink
Standalone NPM packages and React Native support (#335)
Browse files Browse the repository at this point in the history
* Add version 4 react-devtools and react-devtools-core packages which support both React Native and e.g. Safari or iframe DOM usage.
* Replaces typed operations arrays with regular arrays in order to support Hermes. This is unfortunate, since in theory a typed array buffer could be more efficiently transferred between frontend and backend for the web extension, but this never actually worked properly in v8, only Spidermonkey, and it fails entirely in Hermes so for the time being- it's been removed.
* Adds support for React Native (paper renderer)
* Adds a style editor for react-native and react-native-web
  • Loading branch information
Brian Vaughn authored Jul 13, 2019
1 parent 39a811d commit 0f2fb5b
Show file tree
Hide file tree
Showing 77 changed files with 3,911 additions and 387 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
/shells/browser/firefox/*.xpi
/shells/browser/firefox/*.pem
/shells/browser/shared/build
/packages/react-devtools-core/dist
/shells/dev/dist
build
node_modules
Expand Down
3 changes: 3 additions & 0 deletions babel.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,9 @@ module.exports = api => {
} else {
targets.chrome = minChromeVersion.toString();
targets.firefox = minFirefoxVersion.toString();

// This targets RN/Hermes.
targets.IE = '11';
}
const plugins = [
['@babel/plugin-transform-flow-strip-types'],
Expand Down
284 changes: 284 additions & 0 deletions fixtures/standalone/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,284 @@
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8" />
<title>TODO List</title>

<!-- DevTools -->
<script src="http://localhost:8097"></script>

<script src="https://unpkg.com/react@16/umd/react.development.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.development.js"></script>

<!-- Don't use this in production: -->
<script src="https://unpkg.com/babel-standalone@6.15.0/babel.min.js"></script>

<style type="text/css">
.Input {
font-size: 1rem;
padding: 0.25rem;
}

.IconButton {
padding: 0.25rem;
border: none;
background: none;
cursor: pointer;
}

.List {
margin: 0.5rem 0 0;
padding: 0;
}

.ListItem {
list-style-type: none;
}

.Label {
cursor: pointer;
padding: 0.25rem;
color: #555;
}
.Label:hover {
color: #000;
}

.IconButton {
padding: 0.25rem;
border: none;
background: none;
cursor: pointer;
}
</style>
</head>
<body>
<div id="root"></div>
<script type="text/babel">
const { Fragment, useCallback, useState } = React;

function List(props) {
const [newItemText, setNewItemText] = useState("");
const [items, setItems] = useState([
{ id: 1, isComplete: true, text: "First" },
{ id: 2, isComplete: true, text: "Second" },
{ id: 3, isComplete: false, text: "Third" }
]);
const [uid, setUID] = useState(4);

const handleClick = useCallback(() => {
if (newItemText !== "") {
setItems([
...items,
{
id: uid,
isComplete: false,
text: newItemText
}
]);
setUID(uid + 1);
setNewItemText("");
}
}, [newItemText, items, uid]);

const handleKeyPress = useCallback(
event => {
if (event.key === "Enter") {
handleClick();
}
},
[handleClick]
);

const handleChange = useCallback(
event => {
setNewItemText(event.currentTarget.value);
},
[setNewItemText]
);

const removeItem = useCallback(
itemToRemove => setItems(items.filter(item => item !== itemToRemove)),
[items]
);

const toggleItem = useCallback(
itemToToggle => {
const index = items.indexOf(itemToToggle);

setItems(
items
.slice(0, index)
.concat({
...itemToToggle,
isComplete: !itemToToggle.isComplete
})
.concat(items.slice(index + 1))
);
},
[items]
);

return (
<Fragment>
<h1>List</h1>
<input
type="text"
placeholder="New list item..."
className="Input"
value={newItemText}
onChange={handleChange}
onKeyPress={handleKeyPress}
/>
<button
className="IconButton"
disabled={newItemText === ""}
onClick={handleClick}
>
<span role="img" aria-label="Add item">
</span>
</button>
<ul className="List">
{items.map(item => (
<ListItem
key={item.id}
item={item}
removeItem={removeItem}
toggleItem={toggleItem}
/>
))}
</ul>
</Fragment>
);
}

function ListItem({ item, removeItem, toggleItem }) {
const handleDelete = useCallback(() => {
removeItem(item);
}, [item, removeItem]);

const handleToggle = useCallback(() => {
toggleItem(item);
}, [item, toggleItem]);

return (
<li className="ListItem">
<button className="IconButton" onClick={handleDelete}>
🗑
</button>
<label className="Label">
<input
className="Input"
checked={item.isComplete}
onChange={handleToggle}
type="checkbox"
/>{" "}
{item.text}
</label>
</li>
);
}

function SimpleValues() {
return (
<ChildComponent
string="abc"
emptyString=""
number={123}
undefined={undefined}
null={null}
nan={NaN}
infinity={Infinity}
true={true}
false={false}
/>
);
}

class Custom {
_number = 42;
get number() {
return this._number;
}
}

function CustomObject() {
return <ChildComponent customObject={new Custom()} />;
}

const object = {
string: "abc",
longString: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKJLMNOPQRSTUVWXYZ1234567890",
emptyString: "",
number: 123,
boolean: true,
undefined: undefined,
null: null
};

function ObjectProps() {
return (
<ChildComponent
object={{
outer: {
inner: object
}
}}
array={["first", "second", "third"]}
objectInArray={[object]}
arrayInObject={{ array: ["first", "second", "third"] }}
deepObject={{
// Known limitation: we won't go deeper than several levels.
// In the future, we might offer a way to request deeper access on demand.
a: {
b: {
c: {
d: {
e: {
f: {
g: {
h: {
i: {
j: 10
}
}
}
}
}
}
}
}
}
}}
/>
);
}

function ChildComponent(props: any) {
return null;
}

function InspectableElements() {
return (
<Fragment>
<SimpleValues />
<ObjectProps />
<CustomObject />
</Fragment>
);
}

function App() {
return (
<Fragment>
<List />
<InspectableElements />
</Fragment>
);
}

ReactDOM.render(<App />, document.getElementById("root"));
</script>
</body>
</html>
94 changes: 94 additions & 0 deletions flow-typed/chrome.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,94 @@
// @flow

declare var chrome: {
devtools: {
network: {
onNavigated: {
addListener: (cb: (url: string) => void) => void,
removeListener: (cb: () => void) => void,
},
},
inspectedWindow: {
eval: (code: string, cb?: (res: any, err: ?Object) => any) => void,
tabId: number,
},
panels: {
create: (
title: string,
icon: string,
filename: string,
cb: (panel: {
onHidden: {
addListener: (cb: (window: Object) => void) => void,
},
onShown: {
addListener: (cb: (window: Object) => void) => void,
},
}) => void
) => void,
themeName: ?string,
},
},
tabs: {
create: (options: Object) => void,
executeScript: (tabId: number, options: Object, fn: () => void) => void,
onUpdated: {
addListener: (
fn: (tabId: number, changeInfo: Object, tab: Object) => void
) => void,
},
query: (options: Object, fn: (tabArray: Array<Object>) => void) => void,
},
browserAction: {
setIcon: (options: {
tabId: number,
path: { [key: string]: string },
}) => void,
setPopup: (options: {
tabId: number,
popup: string,
}) => void,
},
runtime: {
getURL: (path: string) => string,
sendMessage: (config: Object) => void,
connect: (
config: Object
) => {
disconnect: () => void,
onMessage: {
addListener: (fn: (message: Object) => void) => void,
},
onDisconnect: {
addListener: (fn: (message: Object) => void) => void,
},
postMessage: (data: Object) => void,
},
onConnect: {
addListener: (
fn: (port: {
name: string,
sender: {
tab: {
id: number,
url: string,
},
},
}) => void
) => void,
},
onMessage: {
addListener: (
fn: (
req: Object,
sender: {
url: string,
tab: {
id: number,
},
}
) => void
) => void,
},
},
};
Loading

0 comments on commit 0f2fb5b

Please sign in to comment.