Bug 1291049 - Attempt to bundle the inspector with webpack draft
authorTom Tromey <tom@tromey.com>
Tue, 09 Aug 2016 10:52:58 -0600
changeset 409301 3bbabde086cda686858282c036a8f5dafe125542
parent 409300 bdf79acb0726bf6e46453d3c94ad1df63a53b3ee
child 409302 6f694d04d39f149fbdd990897e3b1e9975730df9
push id28439
push userbmo:ttromey@mozilla.com
push dateFri, 02 Sep 2016 16:21:46 +0000
bugs1291049
milestone51.0a1
Bug 1291049 - Attempt to bundle the inspector with webpack MozReview-Commit-ID: Lk5pRrDCLV4
.hgignore
addon-sdk/source/lib/toolkit/loader.js
devtools/client/.babelrc
devtools/client/inspector/inspector-panel.js
devtools/client/inspector/inspector.xhtml
devtools/client/package.json
devtools/client/webpack.config.js
devtools/client/webpack/prefs-loader.js
devtools/client/webpack/rewrite-event-emitter.js
devtools/shared/l10n.js
--- a/.hgignore
+++ b/.hgignore
@@ -70,16 +70,18 @@
 ^python/psutil/.*\.so
 ^python/psutil/.*\.pyd
 ^python/psutil/build/
 
 # Git repositories
 .git/
 
 # Ignore chrome.manifest files from the devtools loader
+^devtools/client/node_modules
+^devtools/client/inspector/inspector.bundle.js
 ^devtools/client/chrome.manifest$
 ^devtools/shared/chrome.manifest$
 
 # Ignore node_modules directories in devtools
 ^devtools/client/.*/node_modules/
 
 # git checkout of libstagefright
 ^media/libstagefright/android$
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -765,16 +765,22 @@ const Require = iced(function Require(lo
   }
 
   // Expose the `resolve` function for this `Require` instance
   require.resolve = _require.resolve = function resolve(id) {
     let { uri } = getRequirements(id);
     return uri;
   }
 
+  require.context = (prefix) => {
+    return (id) => {
+      return require(prefix + id);
+    };
+  };
+
   // Make `require.main === module` evaluate to true in main module scope.
   require.main = loader.main === requirer ? requirer : undefined;
   return iced(require);
 });
 Loader.Require = Require;
 
 const main = iced(function main(loader, id) {
   // If no main entry provided, and native loader is used,
new file mode 100644
--- /dev/null
+++ b/devtools/client/.babelrc
@@ -0,0 +1,1 @@
+ { "presets": [ "es2015" ] }
\ No newline at end of file
--- a/devtools/client/inspector/inspector-panel.js
+++ b/devtools/client/inspector/inspector-panel.js
@@ -597,23 +597,23 @@ InspectorPanel.prototype = {
 
   _selectionCssSelector: null,
 
   /**
    * Set the currently selected node unique css selector.
    * Will store the current target url along with it to allow pre-selection at
    * reload
    */
-  set selectionCssSelector(cssSelector = null) {
+  set selectionCssSelector(cssSelector) {
     if (this._panelDestroyer) {
       return;
     }
 
     this._selectionCssSelector = {
-      selector: cssSelector,
+      selector: cssSelector || null,
       url: this._target.url
     };
   },
 
   /**
    * Get the current selection unique css selector if any, that is, if a node
    * is actually selected and that node has been selected while on the same url
    */
new file mode 100644
--- /dev/null
+++ b/devtools/client/inspector/inspector.xhtml
@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!-- 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/. -->
+<html xmlns="http://www.w3.org/1999/xhtml">
+<head>
+  <meta charset="UTF-8" />
+</head>
+<body>
+  <p>
+    Temp inspector
+  </p>
+  <script src="inspector.bundle.js"></script>
+</body>
+</html>
\ No newline at end of file
new file mode 100644
--- /dev/null
+++ b/devtools/client/package.json
@@ -0,0 +1,21 @@
+{
+  "name": "inspector",
+  "version": "1.0.0",
+  "description": "",
+  "main": "",
+  "directories": {
+    "test": "test"
+  },
+  "scripts": {
+    "build": "webpack"
+  },
+  "author": "",
+  "license": "",
+  "devDependencies": {
+    "babel-core": "^6.11.4",
+    "babel-loader": "^6.2.4",
+    "babel-preset-es2015": "^6.9.0",
+    "raw-loader": "^0.5.1",
+    "webpack": "^1.13.1"
+  }
+}
new file mode 100644
--- /dev/null
+++ b/devtools/client/webpack.config.js
@@ -0,0 +1,104 @@
+/* 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";
+
+/* Note that this file is a work in progress attempt for bundling the inspector
+   with webpack.
+
+   Instructions for building:
+    cd devtools/client
+    npm install
+    webpack
+    open inspector/inspector.xhtml in a browser
+*/
+
+const path = require("path");
+const webpack = require("webpack");
+
+module.exports = {
+  bail: true,
+
+  entry: './inspector/inspector-panel.js',
+  output: {
+    filename: './inspector/inspector.bundle.js',
+  },
+  module: {
+    // Disable handling of unknown requires
+    unknownContextRegExp: /$^/,
+    unknownContextCritical: false,
+
+    // Disable handling of requires with a single expression
+    exprContextRegExp: /$^/,
+    exprContextCritical: false,
+
+    // Warn for every expression in require
+    wrappedContextCritical: true,
+
+    loaders: [
+      {
+        test: /event-emitter/,
+        exclude: /node_modules/,
+        loaders: [__dirname + '/webpack/rewrite-event-emitter'],
+      },
+    ]
+  },
+  resolveLoader: {
+    root: [
+      path.resolve('./node_modules'),
+      path.resolve("./webpack"),
+    ]
+  },
+  resolve: {
+    alias: {
+      "devtools-shared/locale": path.join(__dirname, "../shared/locales/en-US"),
+      "devtools/locale": path.join(__dirname, "./locales/en-US"),
+      "global/locale": path.join(__dirname, "../../toolkit/locales/en-US/chrome/global"),
+      "devtools/shared/platform": path.join(__dirname, "../shared/platform/content"),
+      devtools: path.join(__dirname, "../"),
+      Services: path.join(__dirname, "./shared/shim/Services.js"),
+      gcli: path.join(__dirname, "../shared/gcli/source/lib/gcli"),
+      acorn: path.join(__dirname, "../shared/acorn"),
+      "acorn/util/walk": path.join(__dirname, "../shared/acorn/walk"),
+      sdk: path.join(__dirname, "../../addon-sdk/source/lib/sdk"),
+      method: path.join(__dirname, "../../addon-sdk/source/lib/method"),
+      "modules/libpref/init/all": path.join(__dirname,
+                                            "../../modules/libpref/init/all.js"),
+    },
+  },
+
+  plugins: [
+    new webpack.DefinePlugin({
+      "isWorker": JSON.stringify(false),
+      "reportError": "console.error",
+      "loader": "{ lazyRequireGetter: () => {} }",
+      "dump": "console.log",
+    }),
+  ],
+
+  externals: [
+    /codemirror\//,
+    /server\//,
+    {
+      "promise": "var Promise",
+
+      // Just trying to get build to work.  These should be removed eventually:
+      "chrome": "{}",
+
+      "resource://gre/modules/XPCOMUtils.jsm": "{}",
+      "resource://devtools/client/styleeditor/StyleEditorUI.jsm": "{}",
+      "resource://devtools/client/styleeditor/StyleEditorUtil.jsm": "{}",
+      "devtools/client/inspector/inspector-panel": "{}",
+      "devtools/server/actors/utils/audionodes.json": "{}",
+
+      "devtools/client/shared/developer-toolbar": "{}",
+
+      // From sdk.
+      "resource://gre/modules/Preferences.jsm": "{}",
+      "@loader/options": "{}",
+      "@loader/unload": "{}",
+    },
+
+  ],
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/webpack/prefs-loader.js
@@ -0,0 +1,52 @@
+// Rewrite devtools.js or all.js, leaving just the relevant pref() calls.
+
+"use strict";
+
+const PREF_RX = new RegExp("^ *pref\\(\"devtools");
+
+module.exports = function (content) {
+  this.cacheable && this.cacheable();
+
+  // If we're reading devtools.js we have to do some reprocessing.
+  // If we're reading all.js we just assume we can dump all the
+  // conditionals.
+  let isDevtools = this.request.endsWith("/devtools.js");
+
+  // This maps the text of a "#if" to its truth value.  This has to
+  // cover all uses of #if in devtools.js.
+  const ifMap = {
+    "#if MOZ_UPDATE_CHANNEL == beta": false,
+    "#if defined(NIGHTLY_BUILD)": false,
+    "#ifdef MOZ_DEV_EDITION": false,
+    "#ifdef RELEASE_BUILD": true,
+  };
+
+  let lines = content.split("\n");
+  let ignoring = false;
+  let newLines = [];
+  let continuation = false;
+  for (let line of lines) {
+    if (line.startsWith("sticky_pref")) {
+      line = line.slice(7);
+    }
+
+    if (isDevtools) {
+      if (line.startsWith("#if")) {
+        if (!(line in ifMap)) {
+          throw new Error("missing line in ifMap: " + line);
+        }
+        ignoring = !ifMap[line];
+      } else if (line.startsWith("#else")) {
+        ignoring = !ignoring;
+      }
+    }
+
+    if (continuation || (!ignoring && PREF_RX.test(line))) {
+      newLines.push(line);
+
+      // The call to pref(...); might span more than one line.
+      continuation = !/\);/.test(line);
+    }
+  }
+  return newLines.join("\n");
+};
new file mode 100644
--- /dev/null
+++ b/devtools/client/webpack/rewrite-event-emitter.js
@@ -0,0 +1,22 @@
+// Remove the header code from event-emitter.js.  This code confuses
+// webpack.
+
+"use strict";
+
+module.exports = function (content) {
+  this.cacheable && this.cacheable();
+
+  let lines = content.split("\n");
+  let ignoring = false;
+  let newLines = [];
+  for (let line of lines) {
+    if (/function \(factory\)/.test(line)) {
+      ignoring = true;
+    } else if (/call\(this, function /.test(line)) {
+      ignoring = false;
+    } else if (!ignoring && line !== "});") {
+      newLines.push(line);
+    }
+  }
+  return newLines.join("\n");
+};
--- a/devtools/shared/l10n.js
+++ b/devtools/shared/l10n.js
@@ -3,27 +3,54 @@
  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
 "use strict";
 
 const parsePropertiesFile = require("devtools/shared/node-properties/node-properties");
 const { sprintf } = require("devtools/shared/sprintfjs/sprintf");
 
 const propertiesMap = {};
 
+// Some shenanigans are needed for LocalizationHelper's dynamic
+// require to work with Webpack.  Here we a create different context
+// require for each possible locale directory.  Then later we use
+// these functions, rather than plain |require|, to load the resource.
+const reqShared = require.context("raw!devtools-shared/locale/",
+                                  true, /^.*\.properties$/);
+const reqClient = require.context("raw!devtools/locale/",
+                                  true, /^.*\.properties$/);
+const reqGlobal = require.context("raw!global/locale/",
+                                  true, /^.*\.properties$/);
+
 /**
  * Memoized getter for properties files that ensures a given url is only required and
  * parsed once.
  *
  * @param {String} url
  *        The URL of the properties file to parse.
  * @return {Object} parsed properties mapped in an object.
  */
 function getProperties(url) {
   if (!propertiesMap[url]) {
-    propertiesMap[url] = parsePropertiesFile(require(`raw!${url}`));
+    // More shenanigans.  Here we take an input like
+    // "devtools-shared/locale/debugger.properties" and decide which
+    // context require function to use.  Despite the string processing
+    // here, in the end a string identical to |url| ends up being
+    // passed to "require".
+    let index = url.lastIndexOf("/");
+    // Turn "mumble/locale/resource.properties" => "./resource.properties".
+    let baseName = "." + url.substr(index);
+    let reqFn;
+    if (/^global/.test(url)) {
+      reqFn = reqGlobal;
+    } else if (/^devtools-shared/.test(url)) {
+      reqFn = reqShared;
+    } else {
+      reqFn = reqClient;
+    }
+    propertiesMap[url] = parsePropertiesFile(reqFn(baseName));
   }
 
   return propertiesMap[url];
 }
 
 /**
  * Localization convenience methods.
  *