Bug 1256757 - Support AMD in tree modules. r=jryans
authorJan Odvarko <odvarko@gmail.com>
Mon, 21 Mar 2016 13:29:17 +0100
changeset 289595 3be9cf3907ad5bbcb0b5cde7dfca5597b4b8096e
parent 289594 216d9ae88a315bed4961b121fb96f930411cf4b2
child 289596 3ddb1c19d1003e3eccc9f82f4664349e8d6b1213
push id30107
push usercbook@mozilla.com
push dateTue, 22 Mar 2016 10:00:23 +0000
treeherdermozilla-central@3587b25bae30 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjryans
bugs1256757
milestone48.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1256757 - Support AMD in tree modules. r=jryans
devtools/client/shared/components/tree/label-cell.js
devtools/client/shared/components/tree/object-provider.js
devtools/client/shared/components/tree/tree-cell.js
devtools/client/shared/components/tree/tree-header.js
devtools/client/shared/components/tree/tree-row.js
devtools/client/shared/components/tree/tree-view.js
--- a/devtools/client/shared/components/tree/label-cell.js
+++ b/devtools/client/shared/components/tree/label-cell.js
@@ -1,52 +1,55 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-// ReactJS
-const React = require("devtools/client/shared/vendor/react");
+// Make this available to both AMD and CJS environments
+define(function(require, exports, module) {
+  // ReactJS
+  const React = require("devtools/client/shared/vendor/react");
 
-// Shortcuts
-const { td, span } = React.DOM;
-const PropTypes = React.PropTypes;
+  // Shortcuts
+  const { td, span } = React.DOM;
+  const PropTypes = React.PropTypes;
 
-/**
- * Render the default cell used for toggle buttons
- */
-var LabelCell = React.createClass({
-  // See the TreeView component for details related
-  // to the 'member' object.
-  propTypes: {
-    member: PropTypes.object.isRequired
-  },
+  /**
+   * Render the default cell used for toggle buttons
+   */
+  let LabelCell = React.createClass({
+    // See the TreeView component for details related
+    // to the 'member' object.
+    propTypes: {
+      member: PropTypes.object.isRequired
+    },
 
-  displayName: "LabelCell",
-
-  render: function() {
-    let member = this.props.member;
-    let level = member.level || 0;
+    displayName: "LabelCell",
 
-    // Compute indentation dynamically. The deeper the item is
-    // inside the hierarchy, the bigger is the left padding.
-    let rowStyle = {
-      "paddingLeft": (level * 16) + "px",
-    };
+    render: function() {
+      let member = this.props.member;
+      let level = member.level || 0;
+
+      // Compute indentation dynamically. The deeper the item is
+      // inside the hierarchy, the bigger is the left padding.
+      let rowStyle = {
+        "paddingLeft": (level * 16) + "px",
+      };
 
-    return (
-      td({
-        className: "treeLabelCell",
-        key: "default",
-        style: rowStyle},
-        span({ className: "treeIcon" }),
-        span({ className: "treeLabel " + member.type + "Label" },
-          member.name
+      return (
+        td({
+          className: "treeLabelCell",
+          key: "default",
+          style: rowStyle},
+          span({ className: "treeIcon" }),
+          span({ className: "treeLabel " + member.type + "Label" },
+            member.name
+          )
         )
-      )
-    );
-  }
+      );
+    }
+  });
+
+  // Exports from this module
+  module.exports = LabelCell;
 });
-
-// Exports from this module
-module.exports = LabelCell;
--- a/devtools/client/shared/components/tree/object-provider.js
+++ b/devtools/client/shared/components/tree/object-provider.js
@@ -1,87 +1,90 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-/**
- * Implementation of the default data provider. A provider is state less
- * object responsible for transformation data (usually a state) to
- * a structure that can be directly consumed by the tree-view component.
- */
-var ObjectProvider = {
-  getChildren: function(object) {
-    let children = [];
+// Make this available to both AMD and CJS environments
+define(function(require, exports, module) {
+  /**
+   * Implementation of the default data provider. A provider is state less
+   * object responsible for transformation data (usually a state) to
+   * a structure that can be directly consumed by the tree-view component.
+   */
+  let ObjectProvider = {
+    getChildren: function(object) {
+      let children = [];
 
-    if (object instanceof ObjectProperty) {
-      object = object.value;
-    }
+      if (object instanceof ObjectProperty) {
+        object = object.value;
+      }
 
-    if (!object) {
-      return [];
-    }
+      if (!object) {
+        return [];
+      }
 
-    if (typeof (object) == "string") {
-      return [];
-    }
+      if (typeof (object) == "string") {
+        return [];
+      }
 
-    for (let prop in object) {
-      try {
-        children.push(new ObjectProperty(prop, object[prop]));
-      } catch (e) {
-        console.error(e);
+      for (let prop in object) {
+        try {
+          children.push(new ObjectProperty(prop, object[prop]));
+        } catch (e) {
+          console.error(e);
+        }
       }
-    }
-    return children;
-  },
+      return children;
+    },
 
-  hasChildren: function(object) {
-    if (object instanceof ObjectProperty) {
-      object = object.value;
-    }
+    hasChildren: function(object) {
+      if (object instanceof ObjectProperty) {
+        object = object.value;
+      }
 
-    if (!object) {
-      return false;
-    }
+      if (!object) {
+        return false;
+      }
 
-    if (typeof object == "string") {
-      return false;
-    }
+      if (typeof object == "string") {
+        return false;
+      }
 
-    if (typeof object !== "object") {
-      return false;
-    }
+      if (typeof object !== "object") {
+        return false;
+      }
 
-    return Object.keys(object).length > 1;
-  },
+      return Object.keys(object).length > 1;
+    },
 
-  getLabel: function(object) {
-    return (object instanceof ObjectProperty) ?
-      object.name : null;
-  },
+    getLabel: function(object) {
+      return (object instanceof ObjectProperty) ?
+        object.name : null;
+    },
+
+    getValue: function(object) {
+      return (object instanceof ObjectProperty) ?
+        object.value : null;
+    },
 
-  getValue: function(object) {
-    return (object instanceof ObjectProperty) ?
-      object.value : null;
-  },
-
-  getKey: function(object) {
-    return (object instanceof ObjectProperty) ?
-      object.name : null;
-  },
+    getKey: function(object) {
+      return (object instanceof ObjectProperty) ?
+        object.name : null;
+    },
 
-  getType: function(object) {
-    return (object instanceof ObjectProperty) ?
-      typeof object.value : typeof object;
-  }
-};
+    getType: function(object) {
+      return (object instanceof ObjectProperty) ?
+        typeof object.value : typeof object;
+    }
+  };
 
-function ObjectProperty(name, value) {
-  this.name = name;
-  this.value = value;
-}
+  function ObjectProperty(name, value) {
+    this.name = name;
+    this.value = value;
+  }
 
-// Exports from this module
-exports.ObjectProperty = ObjectProperty;
-exports.ObjectProvider = ObjectProvider;
+  // Exports from this module
+  exports.ObjectProperty = ObjectProperty;
+  exports.ObjectProvider = ObjectProvider;
+});
--- a/devtools/client/shared/components/tree/tree-cell.js
+++ b/devtools/client/shared/components/tree/tree-cell.js
@@ -1,96 +1,99 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-const React = require("devtools/client/shared/vendor/react");
-
-// Shortcuts
-const { td, span } = React.DOM;
-const PropTypes = React.PropTypes;
+// Make this available to both AMD and CJS environments
+define(function(require, exports, module) {
+  const React = require("devtools/client/shared/vendor/react");
 
-/**
- * This template represents a cell in TreeView row. It's rendered
- * using <td> element (the row is <tr> and the entire tree is <table>).
- */
-var TreeCell = React.createClass({
-  // See TreeView component for detailed property explanation.
-  propTypes: {
-    value: PropTypes.any,
-    decorator: PropTypes.object,
-    id: PropTypes.string.isRequired,
-    member: PropTypes.object.isRequired,
-    renderValue: PropTypes.func.isRequired
-  },
-
-  displayName: "TreeCell",
+  // Shortcuts
+  const { td, span } = React.DOM;
+  const PropTypes = React.PropTypes;
 
   /**
-   * Optimize cell rendering. If value is the same do not render.
+   * This template represents a cell in TreeView row. It's rendered
+   * using <td> element (the row is <tr> and the entire tree is <table>).
    */
-  shouldComponentUpdate: function(nextProps) {
-    return (this.props.value != nextProps.value);
-  },
+  let TreeCell = React.createClass({
+    // See TreeView component for detailed property explanation.
+    propTypes: {
+      value: PropTypes.any,
+      decorator: PropTypes.object,
+      id: PropTypes.string.isRequired,
+      member: PropTypes.object.isRequired,
+      renderValue: PropTypes.func.isRequired
+    },
 
-  getCellClass: function(object, id) {
-    let decorator = this.props.decorator;
-    if (!decorator || !decorator.getCellClass) {
-      return [];
-    }
+    displayName: "TreeCell",
 
-    // Decorator can return a simple string or array of strings.
-    let classNames = decorator.getCellClass(object, id);
-    if (!classNames) {
-      return [];
-    }
+    /**
+     * Optimize cell rendering. If value is the same do not render.
+     */
+    shouldComponentUpdate: function(nextProps) {
+      return (this.props.value != nextProps.value);
+    },
 
-    if (typeof classNames == "string") {
-      classNames = [classNames];
-    }
+    getCellClass: function(object, id) {
+      let decorator = this.props.decorator;
+      if (!decorator || !decorator.getCellClass) {
+        return [];
+      }
 
-    return classNames;
-  },
+      // Decorator can return a simple string or array of strings.
+      let classNames = decorator.getCellClass(object, id);
+      if (!classNames) {
+        return [];
+      }
 
-  render: function() {
-    let member = this.props.member;
-    let type = member.type || "";
-    let id = this.props.id;
-    let value = this.props.value;
-    let decorator = this.props.decorator;
+      if (typeof classNames == "string") {
+        classNames = [classNames];
+      }
+
+      return classNames;
+    },
 
-    // Compute class name list for the <td> element.
-    let classNames = this.getCellClass(member.object, id) || [];
-    classNames.push("treeValueCell");
-    classNames.push(type + "Cell");
+    render: function() {
+      let member = this.props.member;
+      let type = member.type || "";
+      let id = this.props.id;
+      let value = this.props.value;
+      let decorator = this.props.decorator;
 
-    // Render value using a default render function or custom
-    // provided function from props or a decorator.
-    let renderValue = this.props.renderValue || defaultRenderValue;
-    if (decorator && decorator.renderValue) {
-      renderValue = decorator.renderValue(member.object, id) || renderValue;
-    }
+      // Compute class name list for the <td> element.
+      let classNames = this.getCellClass(member.object, id) || [];
+      classNames.push("treeValueCell");
+      classNames.push(type + "Cell");
 
-    let props = Object.assign({}, this.props, {
-      object: value,
-    });
+      // Render value using a default render function or custom
+      // provided function from props or a decorator.
+      let renderValue = this.props.renderValue || defaultRenderValue;
+      if (decorator && decorator.renderValue) {
+        renderValue = decorator.renderValue(member.object, id) || renderValue;
+      }
 
-    // Render me!
-    return (
-      td({ className: classNames.join(" ") },
-        span({}, renderValue(props))
-      )
-    );
-  }
-});
+      let props = Object.assign({}, this.props, {
+        object: value,
+      });
 
-// Default value rendering.
-var defaultRenderValue = props => {
-  return (
-    props.object + ""
-  );
-};
+      // Render me!
+      return (
+        td({ className: classNames.join(" ") },
+          span({}, renderValue(props))
+        )
+      );
+    }
+  });
 
-// Exports from this module
-module.exports = TreeCell;
+  // Default value rendering.
+  let defaultRenderValue = props => {
+    return (
+      props.object + ""
+    );
+  };
+
+  // Exports from this module
+  module.exports = TreeCell;
+});
--- a/devtools/client/shared/components/tree/tree-header.js
+++ b/devtools/client/shared/components/tree/tree-header.js
@@ -1,97 +1,100 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-// ReactJS
-const React = require("devtools/client/shared/vendor/react");
-
-// Shortcuts
-const { thead, tr, td, div } = React.DOM;
-const PropTypes = React.PropTypes;
+// Make this available to both AMD and CJS environments
+define(function(require, exports, module) {
+  // ReactJS
+  const React = require("devtools/client/shared/vendor/react");
 
-/**
- * This component is responsible for rendering tree header.
- * It's based on <thead> element.
- */
-var TreeHeader = React.createClass({
-  // See also TreeView component for detailed info about properties.
-  propTypes: {
-    // Custom tree decorator
-    decorator: PropTypes.object,
-    // True if the header should be visible
-    header: PropTypes.bool,
-    // Array with column definition
-    columns: PropTypes.array
-  },
-
-  displayName: "TreeHeader",
-
-  getDefaultProps: function() {
-    return {
-      columns: [{
-        id: "default"
-      }]
-    };
-  },
+  // Shortcuts
+  const { thead, tr, td, div } = React.DOM;
+  const PropTypes = React.PropTypes;
 
-  getHeaderClass: function(colId) {
-    let decorator = this.props.decorator;
-    if (!decorator || !decorator.getHeaderClass) {
-      return [];
-    }
+  /**
+   * This component is responsible for rendering tree header.
+   * It's based on <thead> element.
+   */
+  let TreeHeader = React.createClass({
+    // See also TreeView component for detailed info about properties.
+    propTypes: {
+      // Custom tree decorator
+      decorator: PropTypes.object,
+      // True if the header should be visible
+      header: PropTypes.bool,
+      // Array with column definition
+      columns: PropTypes.array
+    },
 
-    // Decorator can return a simple string or array of strings.
-    let classNames = decorator.getHeaderClass(colId);
-    if (!classNames) {
-      return [];
-    }
-
-    if (typeof classNames == "string") {
-      classNames = [classNames];
-    }
+    displayName: "TreeHeader",
 
-    return classNames;
-  },
-
-  render: function() {
-    let cells = [];
-    let visible = this.props.header;
+    getDefaultProps: function() {
+      return {
+        columns: [{
+          id: "default"
+        }]
+      };
+    },
 
-    // Render the rest of the columns (if any)
-    this.props.columns.forEach(col => {
-      let cellStyle = {
-        "width": col.width ? col.width : "",
-      };
+    getHeaderClass: function(colId) {
+      let decorator = this.props.decorator;
+      if (!decorator || !decorator.getHeaderClass) {
+        return [];
+      }
 
-      let classNames = [];
-
-      if (visible) {
-        classNames = this.getHeaderClass(col.id);
-        classNames.push("treeHeaderCell");
+      // Decorator can return a simple string or array of strings.
+      let classNames = decorator.getHeaderClass(colId);
+      if (!classNames) {
+        return [];
       }
 
-      cells.push(
-        td({
-          className: classNames.join(" "),
-          style: cellStyle,
-          key: col.id},
-          div({ className: visible ? "treeHeaderCellBox" : "" },
-            visible ? col.title : ""
+      if (typeof classNames == "string") {
+        classNames = [classNames];
+      }
+
+      return classNames;
+    },
+
+    render: function() {
+      let cells = [];
+      let visible = this.props.header;
+
+      // Render the rest of the columns (if any)
+      this.props.columns.forEach(col => {
+        let cellStyle = {
+          "width": col.width ? col.width : "",
+        };
+
+        let classNames = [];
+
+        if (visible) {
+          classNames = this.getHeaderClass(col.id);
+          classNames.push("treeHeaderCell");
+        }
+
+        cells.push(
+          td({
+            className: classNames.join(" "),
+            style: cellStyle,
+            key: col.id},
+            div({ className: visible ? "treeHeaderCellBox" : "" },
+              visible ? col.title : ""
+            )
           )
-        )
+        );
+      });
+
+      return (
+        thead({}, tr({ className: visible ? "treeHeaderRow" : "" },
+          cells
+        ))
       );
-    });
+    }
+  });
 
-    return (
-      thead({}, tr({ className: visible ? "treeHeaderRow" : "" },
-        cells
-      ))
-    );
-  }
+  // Exports from this module
+  module.exports = TreeHeader;
 });
-
-// Exports from this module
-module.exports = TreeHeader;
--- a/devtools/client/shared/components/tree/tree-row.js
+++ b/devtools/client/shared/components/tree/tree-row.js
@@ -1,181 +1,184 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-// ReactJS
-const React = require("devtools/client/shared/vendor/react");
-const ReactDOM = require("devtools/client/shared/vendor/react-dom");
-
-// Tree
-const TreeCell = React.createFactory(require("./tree-cell"));
-const LabelCell = React.createFactory(require("./label-cell"));
-
-// Shortcuts
-const { tr } = React.DOM;
-const PropTypes = React.PropTypes;
+// Make this available to both AMD and CJS environments
+define(function(require, exports, module) {
+  // ReactJS
+  const React = require("devtools/client/shared/vendor/react");
+  const ReactDOM = require("devtools/client/shared/vendor/react-dom");
 
-/**
- * This template represents a node in TreeView component. It's rendered
- * using <tr> element (the entire tree is one big <table>).
- */
-var TreeRow = React.createClass({
-  // See TreeView component for more details about the props and
-  // the 'member' object.
-  propTypes: {
-    member: PropTypes.shape({
-      object: PropTypes.obSject,
-      name: PropTypes.sring,
-      type: PropTypes.string.isRequired,
-      rowClass: PropTypes.string.isRequired,
-      level: PropTypes.number.isRequired,
-      hasChildren: PropTypes.bool,
-      value: PropTypes.any,
-      open: PropTypes.bool.isRequired,
-      path: PropTypes.string.isRequired,
-      hidden: PropTypes.bool,
-    }),
-    decorator: PropTypes.object,
-    renderCell: PropTypes.object,
-    renderLabelCell: PropTypes.object,
-    columns: PropTypes.array.isRequired,
-    provider: PropTypes.object.isRequired,
-    onClick: PropTypes.func.isRequired
-  },
+  // Tree
+  const TreeCell = React.createFactory(require("./tree-cell"));
+  const LabelCell = React.createFactory(require("./label-cell"));
 
-  displayName: "TreeRow",
+  // Shortcuts
+  const { tr } = React.DOM;
+  const PropTypes = React.PropTypes;
 
   /**
-   * Optimize row rendering. If props are the same do not render.
-   * This makes the rendering a lot faster!
+   * This template represents a node in TreeView component. It's rendered
+   * using <tr> element (the entire tree is one big <table>).
    */
-  shouldComponentUpdate: function(nextProps) {
-    let props = ["name", "open", "value", "loading"];
-    for (let p in props) {
-      if (nextProps.member[props[p]] != this.props.member[props[p]]) {
-        return true;
-      }
-    }
+  let TreeRow = React.createClass({
+    // See TreeView component for more details about the props and
+    // the 'member' object.
+    propTypes: {
+      member: PropTypes.shape({
+        object: PropTypes.obSject,
+        name: PropTypes.sring,
+        type: PropTypes.string.isRequired,
+        rowClass: PropTypes.string.isRequired,
+        level: PropTypes.number.isRequired,
+        hasChildren: PropTypes.bool,
+        value: PropTypes.any,
+        open: PropTypes.bool.isRequired,
+        path: PropTypes.string.isRequired,
+        hidden: PropTypes.bool,
+      }),
+      decorator: PropTypes.object,
+      renderCell: PropTypes.object,
+      renderLabelCell: PropTypes.object,
+      columns: PropTypes.array.isRequired,
+      provider: PropTypes.object.isRequired,
+      onClick: PropTypes.func.isRequired
+    },
 
-    return false;
-  },
+    displayName: "TreeRow",
 
-  componentWillReceiveProps(nextProps) {
-    // I don't like accessing the underlying DOM elements directly,
-    // but this optimization makes the filtering so damn fast!
-    // The row doesn't have to be re-rendered, all we really need
-    // to do is toggling a class name.
-    // The important part is that DOM elements don't need to be
-    // re-created when they should appear again.
-    if (nextProps.member.hidden != this.props.member.hidden) {
-      let row = ReactDOM.findDOMNode(this);
-      row.classList.toggle("hidden");
-    }
-  },
+    /**
+     * Optimize row rendering. If props are the same do not render.
+     * This makes the rendering a lot faster!
+     */
+    shouldComponentUpdate: function(nextProps) {
+      let props = ["name", "open", "value", "loading"];
+      for (let p in props) {
+        if (nextProps.member[props[p]] != this.props.member[props[p]]) {
+          return true;
+        }
+      }
+
+      return false;
+    },
 
-  getRowClass: function(object) {
-    let decorator = this.props.decorator;
-    if (!decorator || !decorator.getRowClass) {
-      return [];
-    }
+    componentWillReceiveProps(nextProps) {
+      // I don't like accessing the underlying DOM elements directly,
+      // but this optimization makes the filtering so damn fast!
+      // The row doesn't have to be re-rendered, all we really need
+      // to do is toggling a class name.
+      // The important part is that DOM elements don't need to be
+      // re-created when they should appear again.
+      if (nextProps.member.hidden != this.props.member.hidden) {
+        let row = ReactDOM.findDOMNode(this);
+        row.classList.toggle("hidden");
+      }
+    },
 
-    // Decorator can return a simple string or array of strings.
-    let classNames = decorator.getRowClass(object);
-    if (!classNames) {
-      return [];
-    }
-
-    if (typeof classNames == "string") {
-      classNames = [classNames];
-    }
-
-    return classNames;
-  },
+    getRowClass: function(object) {
+      let decorator = this.props.decorator;
+      if (!decorator || !decorator.getRowClass) {
+        return [];
+      }
 
-  render: function() {
-    let member = this.props.member;
-    let decorator = this.props.decorator;
+      // Decorator can return a simple string or array of strings.
+      let classNames = decorator.getRowClass(object);
+      if (!classNames) {
+        return [];
+      }
 
-    // Compute class name list for the <tr> element.
-    let classNames = this.getRowClass(member.object) || [];
-    classNames.push("treeRow");
-    classNames.push(member.type + "Row");
+      if (typeof classNames == "string") {
+        classNames = [classNames];
+      }
+
+      return classNames;
+    },
+
+    render: function() {
+      let member = this.props.member;
+      let decorator = this.props.decorator;
 
-    if (member.hasChildren) {
-      classNames.push("hasChildren");
-    }
+      // Compute class name list for the <tr> element.
+      let classNames = this.getRowClass(member.object) || [];
+      classNames.push("treeRow");
+      classNames.push(member.type + "Row");
 
-    if (member.open) {
-      classNames.push("opened");
-    }
+      if (member.hasChildren) {
+        classNames.push("hasChildren");
+      }
 
-    if (member.loading) {
-      classNames.push("loading");
-    }
+      if (member.open) {
+        classNames.push("opened");
+      }
+
+      if (member.loading) {
+        classNames.push("loading");
+      }
 
-    if (member.hidden) {
-      classNames.push("hidden");
-    }
+      if (member.hidden) {
+        classNames.push("hidden");
+      }
 
-    // The label column (with toggle buttons) is usually
-    // the first one, but there might be cases (like in
-    // the Memory panel) where the toggling is done
-    // in the last column.
-    let cells = [];
+      // The label column (with toggle buttons) is usually
+      // the first one, but there might be cases (like in
+      // the Memory panel) where the toggling is done
+      // in the last column.
+      let cells = [];
+
+      // Get components for rendering cells.
+      let renderCell = this.props.renderCell || RenderCell;
+      let renderLabelCell = this.props.renderLabelCell || RenderLabelCell;
+      if (decorator && decorator.renderLabelCell) {
+        renderLabelCell = decorator.renderLabelCell(member.object) ||
+          renderLabelCell;
+      }
 
-    // Get components for rendering cells.
-    let renderCell = this.props.renderCell || RenderCell;
-    let renderLabelCell = this.props.renderLabelCell || RenderLabelCell;
-    if (decorator && decorator.renderLabelCell) {
-      renderLabelCell = decorator.renderLabelCell(member.object) ||
-        renderLabelCell;
-    }
+      // Render a cell for every column.
+      this.props.columns.forEach(col => {
+        let props = Object.assign({}, this.props, {
+          key: col.id,
+          id: col.id,
+          value: this.props.provider.getValue(member.object, col.id)
+        });
 
-    // Render a cell for every column.
-    this.props.columns.forEach(col => {
-      let props = Object.assign({}, this.props, {
-        key: col.id,
-        id: col.id,
-        value: this.props.provider.getValue(member.object, col.id)
+        if (decorator && decorator.renderCell) {
+          renderCell = decorator.renderCell(member.object, col.id);
+        }
+
+        let render = (col.id == "default") ? renderLabelCell : renderCell;
+
+        // Some cells don't have to be rendered. This happens when some
+        // other cells span more columns. Note that the label cells contains
+        // toggle buttons and should be usually there unless we are rendering
+        // a simple non-expandable table.
+        if (render) {
+          cells.push(render(props));
+        }
       });
 
-      if (decorator && decorator.renderCell) {
-        renderCell = decorator.renderCell(member.object, col.id);
-      }
-
-      let render = (col.id == "default") ? renderLabelCell : renderCell;
-
-      // Some cells don't have to be rendered. This happens when some
-      // other cells span more columns. Note that the label cells contains
-      // toggle buttons and should be usually there unless we are rendering
-      // a simple non-expandable table.
-      if (render) {
-        cells.push(render(props));
-      }
-    });
+      // Render tree row
+      return (
+        tr({
+          className: classNames.join(" "),
+          onClick: this.props.onClick},
+          cells
+        )
+      );
+    }
+  });
 
-    // Render tree row
-    return (
-      tr({
-        className: classNames.join(" "),
-        onClick: this.props.onClick},
-        cells
-      )
-    );
-  }
-});
+  // Helpers
+
+  let RenderCell = props => {
+    return TreeCell(props);
+  };
 
-// Helpers
-
-var RenderCell = props => {
-  return TreeCell(props);
-};
+  let RenderLabelCell = props => {
+    return LabelCell(props);
+  };
 
-var RenderLabelCell = props => {
-  return LabelCell(props);
-};
-
-// Exports from this module
-module.exports = TreeRow;
+  // Exports from this module
+  module.exports = TreeRow;
+});
--- a/devtools/client/shared/components/tree/tree-view.js
+++ b/devtools/client/shared/components/tree/tree-view.js
@@ -1,348 +1,352 @@
 /* -*- indent-tabs-mode: nil; js-indent-level: 2 -*- */
 /* vim: set ft=javascript ts=2 et sw=2 tw=80: */
 /* This Source Code Form is subject to the terms of the Mozilla Public
  * License, v. 2.0. If a copy of the MPL was not distributed with this
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
-// ReactJS
-const React = require("devtools/client/shared/vendor/react");
-
-// Reps
-const { ObjectProvider } = require("./object-provider");
-const TreeRow = React.createFactory(require("./tree-row"));
-const TreeHeader = React.createFactory(require("./tree-header"));
-
-// Shortcuts
-const DOM = React.DOM;
-const PropTypes = React.PropTypes;
+// Make this available to both AMD and CJS environments
+define(function(require, exports, module) {
+  // ReactJS
+  const React = require("devtools/client/shared/vendor/react");
 
-/**
- * This component represents a tree view with expandable/collapsible nodes.
- * The tree is rendered using <table> element where every node is represented
- * by <tr> element. The tree is one big table where nodes (rows) are properly
- * indented from the left to mimic hierarchical structure of the data.
- *
- * The tree can have arbitrary number of columns and so, might be use
- * as an expandable tree-table UI widget as well. By default, there is
- * one column for node label and one for node value.
- *
- * The tree is maintaining its (presentation) state, which consists
- * from list of expanded nodes and list of columns.
- *
- * Complete data provider interface:
- * var TreeProvider = {
- *   getChildren: function(object);
- *   hasChildren: function(object);
- *   getLabel: function(object, colId);
- *   getValue: function(object, colId);
- *   getKey: function(object);
- *   getType: function(object);
- * }
- *
- * Complete tree decorator interface:
- * var TreeDecorator = {
- *   getRowClass: function(object);
- *   getCellClass: function(object, colId);
- *   getHeaderClass: function(colId);
- *   renderValue: function(object, colId);
- *   renderRow: function(object);
- *   renderCelL: function(object, colId);
- *   renderLabelCell: function(object);
- * }
- */
-var TreeView = React.createClass({
-  // The only required property (not set by default) is the input data
-  // object that is used to puputate the tree.
-  propTypes: {
-    // The input data object.
-    object: PropTypes.any,
-    className: PropTypes.string,
-    // Data provider (see also the interface above)
-    provider: PropTypes.shape({
-      getChildren: PropTypes.func,
-      hasChildren: PropTypes.func,
-      getLabel: PropTypes.func,
-      getValue: PropTypes.func,
-      getKey: PropTypes.func,
-      getType: PropTypes.func,
-    }).isRequired,
-    // Tree decorator (see also the interface above)
-    decorator: PropTypes.shape({
-      getRowClass: PropTypes.func,
-      getCellClass: PropTypes.func,
-      getHeaderClass: PropTypes.func,
-      renderValue: PropTypes.func,
-      renderRow: PropTypes.func,
-      renderCelL: PropTypes.func,
-      renderLabelCell: PropTypes.func,
-    }),
-    // Custom tree row (node) renderer
-    renderRow: PropTypes.func,
-    // Custom cell renderer
-    renderCell: PropTypes.func,
-    // Custom value renderef
-    renderValue: PropTypes.func,
-    // Custom tree label (including a toggle button) renderer
-    renderLabelCell: PropTypes.func,
-    // Set of expanded nodes
-    expandedNodes: PropTypes.object,
-    // Custom filtering callback
-    onFilter: PropTypes.func,
-    // Custom sorting callback
-    onSort: PropTypes.func,
-    // A header is displayed if set to true
-    header: PropTypes.bool,
-    // Array of columns
-    columns: PropTypes.arrayOf(PropTypes.shape({
-      id: PropTypes.string.isRequired,
-      title: PropTypes.string,
-      width: PropTypes.string
-    }))
-  },
+  // Reps
+  const { ObjectProvider } = require("./object-provider");
+  const TreeRow = React.createFactory(require("./tree-row"));
+  const TreeHeader = React.createFactory(require("./tree-header"));
 
-  displayName: "TreeView",
-
-  getDefaultProps: function() {
-    return {
-      object: null,
-      renderRow: null,
-      provider: ObjectProvider,
-      expandedNodes: new Set(),
-      columns: []
-    };
-  },
-
-  getInitialState: function() {
-    return {
-      expandedNodes: this.props.expandedNodes,
-      columns: ensureDefaultColumn(this.props.columns)
-    };
-  },
-
-  // Node expand/collapse
-
-  toggle: function(nodePath) {
-    let nodes = this.state.expandedNodes;
-    if (this.isExpanded(nodePath)) {
-      nodes.delete(nodePath);
-    } else {
-      nodes.add(nodePath);
-    }
-
-    // Compute new state and update the tree.
-    this.setState(Object.assign({}, this.state, {
-      expandedNodes: nodes
-    }));
-  },
-
-  isExpanded: function(nodePath) {
-    return this.state.expandedNodes.has(nodePath);
-  },
-
-  // Event Handlers
-
-  onClickRow: function(nodePath, event) {
-    event.stopPropagation();
-    this.toggle(nodePath);
-  },
-
-  // Filtering & Sorting
+  // Shortcuts
+  const DOM = React.DOM;
+  const PropTypes = React.PropTypes;
 
   /**
-   * Filter out nodes that don't correspond to the current filter.
-   * @return {Boolean} true if the node should be visible otherwise false.
-   */
-  onFilter: function(object) {
-    let onFilter = this.props.onFilter;
-    return onFilter ? onFilter(object) : true;
-  },
-
-  onSort: function(parent, children) {
-    let onSort = this.props.onSort;
-    return onSort ? onSort(parent, children) : children;
-  },
-
-  // Members
-
-  /**
-   * Return children node objects (so called 'members') for given
-   * parent object.
+   * This component represents a tree view with expandable/collapsible nodes.
+   * The tree is rendered using <table> element where every node is represented
+   * by <tr> element. The tree is one big table where nodes (rows) are properly
+   * indented from the left to mimic hierarchical structure of the data.
+   *
+   * The tree can have arbitrary number of columns and so, might be use
+   * as an expandable tree-table UI widget as well. By default, there is
+   * one column for node label and one for node value.
+   *
+   * The tree is maintaining its (presentation) state, which consists
+   * from list of expanded nodes and list of columns.
+   *
+   * Complete data provider interface:
+   * var TreeProvider = {
+   *   getChildren: function(object);
+   *   hasChildren: function(object);
+   *   getLabel: function(object, colId);
+   *   getValue: function(object, colId);
+   *   getKey: function(object);
+   *   getType: function(object);
+   * }
+   *
+   * Complete tree decorator interface:
+   * var TreeDecorator = {
+   *   getRowClass: function(object);
+   *   getCellClass: function(object, colId);
+   *   getHeaderClass: function(colId);
+   *   renderValue: function(object, colId);
+   *   renderRow: function(object);
+   *   renderCelL: function(object, colId);
+   *   renderLabelCell: function(object);
+   * }
    */
-  getMembers: function(parent, level, path) {
-    // Strings don't have children. Note that 'long' strings are using
-    // the expander icon (+/-) to display the entire original value,
-    // but there are no child items.
-    if (typeof parent == "string") {
-      return [];
-    }
-
-    let provider = this.props.provider;
-    let children = provider.getChildren(parent) || [];
+  let TreeView = React.createClass({
+    // The only required property (not set by default) is the input data
+    // object that is used to puputate the tree.
+    propTypes: {
+      // The input data object.
+      object: PropTypes.any,
+      className: PropTypes.string,
+      // Data provider (see also the interface above)
+      provider: PropTypes.shape({
+        getChildren: PropTypes.func,
+        hasChildren: PropTypes.func,
+        getLabel: PropTypes.func,
+        getValue: PropTypes.func,
+        getKey: PropTypes.func,
+        getType: PropTypes.func,
+      }).isRequired,
+      // Tree decorator (see also the interface above)
+      decorator: PropTypes.shape({
+        getRowClass: PropTypes.func,
+        getCellClass: PropTypes.func,
+        getHeaderClass: PropTypes.func,
+        renderValue: PropTypes.func,
+        renderRow: PropTypes.func,
+        renderCelL: PropTypes.func,
+        renderLabelCell: PropTypes.func,
+      }),
+      // Custom tree row (node) renderer
+      renderRow: PropTypes.func,
+      // Custom cell renderer
+      renderCell: PropTypes.func,
+      // Custom value renderef
+      renderValue: PropTypes.func,
+      // Custom tree label (including a toggle button) renderer
+      renderLabelCell: PropTypes.func,
+      // Set of expanded nodes
+      expandedNodes: PropTypes.object,
+      // Custom filtering callback
+      onFilter: PropTypes.func,
+      // Custom sorting callback
+      onSort: PropTypes.func,
+      // A header is displayed if set to true
+      header: PropTypes.bool,
+      // Array of columns
+      columns: PropTypes.arrayOf(PropTypes.shape({
+        id: PropTypes.string.isRequired,
+        title: PropTypes.string,
+        width: PropTypes.string
+      }))
+    },
 
-    // If the return value is non-array, the children
-    // are being loaded asynchronously.
-    if (!Array.isArray(children)) {
-      return children;
-    }
+    displayName: "TreeView",
 
-    children = this.onSort(parent, children) || children;
+    getDefaultProps: function() {
+      return {
+        object: null,
+        renderRow: null,
+        provider: ObjectProvider,
+        expandedNodes: new Set(),
+        columns: []
+      };
+    },
 
-    return children.map(child => {
-      let key = provider.getKey(child);
-      let nodePath = path + "/" + key;
-      let type = provider.getType(child);
-      let hasChildren = provider.hasChildren(child);
+    getInitialState: function() {
+      return {
+        expandedNodes: this.props.expandedNodes,
+        columns: ensureDefaultColumn(this.props.columns)
+      };
+    },
 
-      // Value with no column specified is used for optimization.
-      // The row is re-rendered only if this value changes.
-      // Value for actual column is get when a cell is rendered.
-      let value = provider.getValue(child);
+    // Node expand/collapse
 
-      if (isLongString(value)) {
-        hasChildren = true;
+    toggle: function(nodePath) {
+      let nodes = this.state.expandedNodes;
+      if (this.isExpanded(nodePath)) {
+        nodes.delete(nodePath);
+      } else {
+        nodes.add(nodePath);
       }
 
-      // Return value is a 'member' object containing meta-data about
-      // tree node. It describes node label, value, type, etc.
-      return {
-        // An object associated with this node.
-        object: child,
-        // A label for the child node
-        name: provider.getLabel(child),
-        // Data type of the child node (used for CSS customization)
-        type: type,
-        // Class attribute computed from the type.
-        rowClass: "treeRow-" + type,
-        // Level of the child within the hierarchy (top == 0)
-        level: level,
-        // True if this node has children.
-        hasChildren: hasChildren,
-        // Value associated with this node (as provided by the data provider)
-        value: value,
-        // True if the node is expanded.
-        open: this.isExpanded(nodePath),
-        // Node path
-        path: nodePath,
-        // True if the node is hidden (used for filtering)
-        hidden: !this.onFilter(child)
-      };
-    });
-  },
+      // Compute new state and update the tree.
+      this.setState(Object.assign({}, this.state, {
+        expandedNodes: nodes
+      }));
+    },
+
+    isExpanded: function(nodePath) {
+      return this.state.expandedNodes.has(nodePath);
+    },
+
+    // Event Handlers
+
+    onClickRow: function(nodePath, event) {
+      event.stopPropagation();
+      this.toggle(nodePath);
+    },
+
+    // Filtering & Sorting
+
+    /**
+     * Filter out nodes that don't correspond to the current filter.
+     * @return {Boolean} true if the node should be visible otherwise false.
+     */
+    onFilter: function(object) {
+      let onFilter = this.props.onFilter;
+      return onFilter ? onFilter(object) : true;
+    },
+
+    onSort: function(parent, children) {
+      let onSort = this.props.onSort;
+      return onSort ? onSort(parent, children) : children;
+    },
+
+    // Members
+
+    /**
+     * Return children node objects (so called 'members') for given
+     * parent object.
+     */
+    getMembers: function(parent, level, path) {
+      // Strings don't have children. Note that 'long' strings are using
+      // the expander icon (+/-) to display the entire original value,
+      // but there are no child items.
+      if (typeof parent == "string") {
+        return [];
+      }
+
+      let provider = this.props.provider;
+      let children = provider.getChildren(parent) || [];
+
+      // If the return value is non-array, the children
+      // are being loaded asynchronously.
+      if (!Array.isArray(children)) {
+        return children;
+      }
+
+      children = this.onSort(parent, children) || children;
+
+      return children.map(child => {
+        let key = provider.getKey(child);
+        let nodePath = path + "/" + key;
+        let type = provider.getType(child);
+        let hasChildren = provider.hasChildren(child);
+
+        // Value with no column specified is used for optimization.
+        // The row is re-rendered only if this value changes.
+        // Value for actual column is get when a cell is rendered.
+        let value = provider.getValue(child);
+
+        if (isLongString(value)) {
+          hasChildren = true;
+        }
 
-  /**
-   * Render tree rows/nodes.
-   */
-  renderRows: function(parent, level = 0, path = "") {
-    let rows = [];
-    let decorator = this.props.decorator;
-    let renderRow = this.props.renderRow || TreeRow;
+        // Return value is a 'member' object containing meta-data about
+        // tree node. It describes node label, value, type, etc.
+        return {
+          // An object associated with this node.
+          object: child,
+          // A label for the child node
+          name: provider.getLabel(child),
+          // Data type of the child node (used for CSS customization)
+          type: type,
+          // Class attribute computed from the type.
+          rowClass: "treeRow-" + type,
+          // Level of the child within the hierarchy (top == 0)
+          level: level,
+          // True if this node has children.
+          hasChildren: hasChildren,
+          // Value associated with this node (as provided by the data provider)
+          value: value,
+          // True if the node is expanded.
+          open: this.isExpanded(nodePath),
+          // Node path
+          path: nodePath,
+          // True if the node is hidden (used for filtering)
+          hidden: !this.onFilter(child)
+        };
+      });
+    },
+
+    /**
+     * Render tree rows/nodes.
+     */
+    renderRows: function(parent, level = 0, path = "") {
+      let rows = [];
+      let decorator = this.props.decorator;
+      let renderRow = this.props.renderRow || TreeRow;
+
+      // Get children for given parent node, iterate over them and render
+      // a row for every one. Use row template (a component) from properties.
+      // If the return value is non-array, the children are being loaded
+      // asynchronously.
+      let members = this.getMembers(parent, level, path);
+      if (!Array.isArray(members)) {
+        return members;
+      }
+
+      members.forEach(member => {
+        if (decorator && decorator.renderRow) {
+          renderRow = decorator.renderRow(member.object) || renderRow;
+        }
 
-    // Get children for given parent node, iterate over them and render
-    // a row for every one. Use row template (a component) from properties.
-    // If the return value is non-array, the children are being loaded
-    // asynchronously.
-    let members = this.getMembers(parent, level, path);
-    if (!Array.isArray(members)) {
-      return members;
-    }
+        let props = Object.assign({}, this.props, {
+          key: member.path,
+          member: member,
+          columns: this.state.columns,
+          onClick: this.onClickRow.bind(this, member.path)
+        });
+
+        // Render single row.
+        rows.push(renderRow(props));
+
+        // If a child node is expanded render its rows too.
+        if (member.hasChildren && member.open) {
+          let childRows = this.renderRows(member.object, level + 1,
+            member.path);
 
-    members.forEach(member => {
-      if (decorator && decorator.renderRow) {
-        renderRow = decorator.renderRow(member.object) || renderRow;
+          // If children needs to be asynchronously fetched first,
+          // set 'loading' property to the parent row. Otherwise
+          // just append children rows to the array of all rows.
+          if (!Array.isArray(childRows)) {
+            let lastIndex = rows.length - 1;
+            props.member.loading = true;
+            rows[lastIndex] = React.cloneElement(rows[lastIndex], props);
+          } else {
+            rows = rows.concat(childRows);
+          }
+        }
+      });
+
+      return rows;
+    },
+
+    render: function() {
+      let root = this.props.object;
+      let classNames = ["treeTable"];
+
+      // Use custom class name from props.
+      let className = this.props.className;
+      if (className) {
+        classNames.push(...className.split(" "));
+      }
+
+      // Alright, let's render all tree rows. The tree is one big <table>.
+      let rows = this.renderRows(root, 0, "");
+
+      // This happens when the view needs to do initial asynchronous
+      // fetch for the root object. The tree might provide a hook API
+      // for rendering animated spinner (just like for tree nodes).
+      if (!Array.isArray(rows)) {
+        rows = [];
       }
 
       let props = Object.assign({}, this.props, {
-        key: member.path,
-        member: member,
-        columns: this.state.columns,
-        onClick: this.onClickRow.bind(this, member.path)
+        columns: this.state.columns
       });
 
-      // Render single row.
-      rows.push(renderRow(props));
-
-      // If a child node is expanded render its rows too.
-      if (member.hasChildren && member.open) {
-        let childRows = this.renderRows(member.object, level + 1, member.path);
+      return (
+        DOM.table({
+          className: classNames.join(" "),
+          cellPadding: 0,
+          cellSpacing: 0},
+          TreeHeader(props),
+          DOM.tbody({},
+            rows
+          )
+        )
+      );
+    }
+  });
 
-        // If children needs to be asynchronously fetched first,
-        // set 'loading' property to the parent row. Otherwise
-        // just append children rows to the array of all rows.
-        if (!Array.isArray(childRows)) {
-          let lastIndex = rows.length - 1;
-          props.member.loading = true;
-          rows[lastIndex] = React.cloneElement(rows[lastIndex], props);
-        } else {
-          rows = rows.concat(childRows);
-        }
-      }
-    });
+  // Helpers
 
-    return rows;
-  },
-
-  render: function() {
-    let root = this.props.object;
-    let classNames = ["treeTable"];
-
-    // Use custom class name from props.
-    let className = this.props.className;
-    if (className) {
-      classNames.push(...className.split(" "));
+  /**
+   * There should always be at least one column (the one with toggle buttons)
+   * and this function ensures that it's true.
+   */
+  function ensureDefaultColumn(columns) {
+    if (!columns) {
+      columns = [];
     }
 
-    // Alright, let's render all tree rows. The tree is one big <table>.
-    let rows = this.renderRows(root, 0, "");
-
-    // This happens when the view needs to do initial asynchronous
-    // fetch for the root object. The tree might provide a hook API
-    // for rendering animated spinner (just like for tree nodes).
-    if (!Array.isArray(rows)) {
-      rows = [];
+    let defaultColumn = columns.filter(col => col.id == "default");
+    if (defaultColumn.length) {
+      return columns;
     }
 
-    let props = Object.assign({}, this.props, {
-      columns: this.state.columns
-    });
-
-    return (
-      DOM.table({
-        className: classNames.join(" "),
-        cellPadding: 0,
-        cellSpacing: 0},
-        TreeHeader(props),
-        DOM.tbody({},
-          rows
-        )
-      )
-    );
-  }
-});
-
-// Helpers
-
-/**
- * There should always be at least one column (the one with toggle buttons)
- * and this function ensures that it's true.
- */
-function ensureDefaultColumn(columns) {
-  if (!columns) {
-    columns = [];
+    // The default column is usually the first one.
+    return [{id: "default"}, ...columns];
   }
 
-  let defaultColumn = columns.filter(col => col.id == "default");
-  if (defaultColumn.length) {
-    return columns;
+  function isLongString(value) {
+    return typeof value == "string" && value.length > 50;
   }
 
-  // The default column is usually the first one.
-  return [{id: "default"}, ...columns];
-}
-
-function isLongString(value) {
-  return typeof value == "string" && value.length > 50;
-}
-
-// Exports from this module
-module.exports = TreeView;
+  // Exports from this module
+  module.exports = TreeView;
+});