Bug 1310702 - use webpack-like require.context in devtools l10n; r?jryans draft
authorTom Tromey <tom@tromey.com>
Thu, 20 Oct 2016 11:00:28 -0600
changeset 429301 872eedc01bd4df7ebd342760a8213fd565a7831e
parent 429300 cd507936d184dd5b8fc71ac52f39b6015e88bbc9
child 534937 7aff96ef68194ac8be3a170f9feb3f2248415c8d
push id33529
push userbmo:ttromey@mozilla.com
push dateTue, 25 Oct 2016 15:58:26 +0000
reviewersjryans
bugs1310702
milestone52.0a1
Bug 1310702 - use webpack-like require.context in devtools l10n; r?jryans MozReview-Commit-ID: 5noH0y0MgaK
addon-sdk/source/lib/toolkit/loader.js
devtools/client/inspector/rules/test/head.js
devtools/client/inspector/shared/test/head.js
devtools/shared/l10n.js
--- a/addon-sdk/source/lib/toolkit/loader.js
+++ b/addon-sdk/source/lib/toolkit/loader.js
@@ -900,16 +900,24 @@ 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;
   }
 
+  // This is like webpack's require.context.  It returns a new require
+  // function that prepends the prefix to any requests.
+  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,
--- a/devtools/client/inspector/rules/test/head.js
+++ b/devtools/client/inspector/rules/test/head.js
@@ -15,17 +15,18 @@ registerCleanupFunction(() => {
 });
 
 var {getInplaceEditorForSpan: inplaceEditor} =
   require("devtools/client/shared/inplace-editor");
 
 const ROOT_TEST_DIR = getRootDirectory(gTestPath);
 const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
 
-const STYLE_INSPECTOR_L10N = new LocalizationHelper("chrome://devtools-shared/locale/styleinspector.properties");
+const STYLE_INSPECTOR_L10N
+      = new LocalizationHelper("devtools-shared/locale/styleinspector.properties");
 
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.defaultColorUnit");
 });
 
 /**
  * The rule-view tests rely on a frame-script to be injected in the content test
  * page. So override the shared-head's addTab to load the frame script after the
--- a/devtools/client/inspector/shared/test/head.js
+++ b/devtools/client/inspector/shared/test/head.js
@@ -16,17 +16,18 @@ var {getInplaceEditorForSpan: inplaceEdi
 const {getColor: getThemeColor} = require("devtools/client/shared/theme");
 
 const TEST_URL_ROOT =
   "http://example.com/browser/devtools/client/inspector/shared/test/";
 const TEST_URL_ROOT_SSL =
   "https://example.com/browser/devtools/client/inspector/shared/test/";
 const ROOT_TEST_DIR = getRootDirectory(gTestPath);
 const FRAME_SCRIPT_URL = ROOT_TEST_DIR + "doc_frame_script.js";
-const STYLE_INSPECTOR_L10N = new LocalizationHelper("chrome://devtools-shared/locale/styleinspector.properties");
+const STYLE_INSPECTOR_L10N =
+      new LocalizationHelper("devtools-shared/locale/styleinspector.properties");
 
 // Clean-up all prefs that might have been changed during a test run
 // (safer here because if the test fails, then the pref is never reverted)
 registerCleanupFunction(() => {
   Services.prefs.clearUserPref("devtools.defaultColorUnit");
 });
 
 /**
--- a/devtools/shared/l10n.js
+++ b/devtools/shared/l10n.js
@@ -3,27 +3,68 @@
  * 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 = {};
 
+// We need some special treatment here for webpack.
+//
+// Webpack doesn't always handle dynamic requires in the best way.  In
+// particular if it sees an unrestricted dynamic require, it will try
+// to put all the files it can find into the generated pack.  (It can
+// also try a bit to parse the expression passed to require, but in
+// our case this doesn't work, because our call below doesn't provide
+// enough information.)
+//
+// Webpack also provides a way around this: require.context.  The idea
+// here is to tell webpack some constraints so that it can include
+// fewer files in the pack.
+//
+// Here we introduce new require contexts for each possible locale
+// directory.  Then we use the correct context to load the property
+// file.  In the webpack case this results in just the locale property
+// files being included in the pack; and in the devtools case this is
+// a wordy no-op.
+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}`));
+    // See the comment above about webpack and require contexts.  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.
  *