docs: function emitLocaleData(compilation, options) {
      if (localeDataEmitted) {
          return localeDataEmitted;
      }
  
      var outputFileName, output;
      var scripts = new Set();
      var normalizations = {};
      var outputDir = path.resolve(options && path.isAbsolute(options.tempDir) ? options.tempDir : path.join(process.cwd(), options.tempDir || 'assets'));
      var sources = {};
  
      var charsets = new Set();
      var charmaps = {};
      var lang2charset;
      var outputSet = {};
      var match;
      var root = options.ilibRoot || findIlibRoot();
      var dataRoot = calcDataRoot(options);
      var manifest = new Set(); // list of all locale data files that were processed
  
      var locales = options.locales;
      locales.forEach(function(locale) {
          var lm = new LocaleMatcher({locale: locale});
          var full = lm.getLikelyLocale();
          if (full.getScript()) {
              scripts.add(full.getScript());
          }
      });
      if (options.debug) console.log("ilib-webpack-plugin: emitting locale data for locales " + locales.join(","));
  
      locales.forEach(function(locale) {
          localeData.forEach(function(filename) {
              normPattern.lastIndex = 0;
              if (filename === "charset" || filename === "charmaps") {
                  // charmaps and charset info are special cases because they are non-locale data.
                  // If they just use the generic "charset" or "charmaps" data, then
                  // we figure out which charsets are appropriate for the locale
                  if (!lang2charset) {
                      var fileName = path.join(dataRoot, "lang2charset.json");
  
                      if (fileName[0] !== '/' && fileName[0] !== '.' ) {
                          fileName = "./" + fileName;
                      }
                      lang2charset = require(fileName);
                  }
  
                  var l = new Locale(locale);
                  var spec = l.getLanguage() + (l.getScript() ? ("-" + l.getScript()) : ""); // TODO: should use l.getLangScript()
                  if (lang2charset[spec]) {
                      // always add the charset, even when charmaps are requested, because the
                      // charmaps need the charset for the charmap
                      lang2charset[spec].forEach(function(charsetName) {
                          charsets.add(charsetName);
                      });
  
                      if (filename === "charmaps") {
                          if (!charmaps[spec]) {
                              charmaps[spec] = new Set();
                          }
                          lang2charset[spec].forEach(function(charsetName) {
                              charmaps[spec].add(charsetName);
                          });
                      }
                  }
              } else if ((match = normPattern.exec(filename)) !== null) {
                  var form = match[1];
                  if (!normalizations[form]) {
                      normalizations[form] = new Set();
                  }
                  if (match.length > 3) {
                      normalizations[form].add(match[3] || "");
                  }
              } else if (filename === "zoneinfo") {
                  // time zone data in the zoneinfo files are a special case because they are non-locale data
                  // console.log(">>>>>>>>>>>>> processing zoneinfo. cwd is " + process.cwd());
                  var cwdToData = path.join(dataRoot, "zoneinfo/zonetab.json");
                  var data = fs.readFileSync(cwdToData, "utf-8");
                  var zonetab = JSON.parse(data);
                  // console.log(">>>>>>>>>>>>> got zone tab.");
                  var line = 'ilib.data.zoneinfo.zonetab = ' + data + ';\n';
                  if (!outputSet.root) {
                      outputSet.root = {};
                  }
                  outputSet.root.zonetab = line;
                  manifest.add("zoneinfo/zonetab.json");
  
                  var regionSet = new Set();
                  locales.forEach(function(locale) {
                      regionSet.add(new Locale(locale).region);
                  });
                  var zoneSet = new Set();
                  regionSet.forEach(function(region) {
                      if (zonetab[region]) {
                          zonetab[region].forEach(function(zone) {
                              zoneSet.add(zone);
                          });
                      }
                  });
                  zoneSet.forEach(function(zone) {
                      try {
                          var cwdToData = path.join(dataRoot, "zoneinfo", zone + ".json");
                          if (fs.existsSync(cwdToData)) {
                              data = fs.readFileSync(cwdToData, "utf-8");
                              var line = 'ilib.data.zoneinfo["' + zone.replace(/-/g, "m").replace(/\+/g, "p") + '"] = ' + data + ';\n';
                              // console.log(">>>>>>>>>>>>> Adding zone: " + line);
                              outputSet.root[zone] = line;
                              manifest.add(path.join("zoneinfo", zone + ".json"));
                          }
                      } catch (e) {
                          console.log("ilib-webpack-plugin: Error: " + e);
                      }
                  }.bind(this));
  
                  // now add the generic zones
                  var zoneinfoDir = path.join(dataRoot, "zoneinfo");
                  var list = fs.readdirSync(zoneinfoDir);
                  list = list.concat(fs.readdirSync(path.join(zoneinfoDir, "Etc")).map(function(zone) {
                      return "Etc/" + zone;
                  }));
  
                  list.filter(function(pathname) {
                      return pathname.endsWith(".json") && pathname !== "zonetab.json";
                  }).forEach(function (file) {
                      var zone = path.basename(file, ".json");
                      var cwdToData = path.join(dataRoot, "zoneinfo", file);
                      data = fs.readFileSync(cwdToData, "utf-8");
                      var line = 'ilib.data.zoneinfo["' + zone.replace(/-/g, "m").replace(/\+/g, "p") + '"] = ' + data + ';\n';
                      // console.log(">>>>>>>>>>>>> Adding generic zone: " + line);
                      // compiler.addDependency(cwdToData);
                      outputSet.root[zone] = line;
                      manifest.add(path.join("zoneinfo", file));
                  }.bind(this));
              } else {
                  var l = new Locale(locale);
  
                  var parts = [
                      ".",
                      l.language
                      ];
  
                  if (l.script) {
                      parts.push(l.language + "/" + l.script);
                      if (l.region) {
                          parts.push(l.language + "/" + l.script + "/" + l.region);
                      }
                  }
                  if (l.region) {
                      parts.push(l.language + "/" + l.region);
                      parts.push("und/" + l.region);
                  }
  
                  parts.forEach(function(localeDir) {
                      try {
                          var cwdToData = path.join(dataRoot, localeDir, filename + ".json");
                          var part = localeDir === "." ? "root" : localeDir;
                          part = part.replace(/\//g, "-");
                          if (!outputSet[part]) {
                              outputSet[part] = {};
                          }
                          if (fs.existsSync(cwdToData)) {
                              if (!outputSet[part][filename]) {
                                  var line = "ilib.data." + toIlibDataName(filename);
                                  if (part !== "root") {
                                      line += "_" + toIlibDataName(part);
                                  }
                                  data = fs.readFileSync(cwdToData, "utf-8");
                                  line += " = " + data + ";\n";
                                  // console.log(">>>>>>>>>>>>> Adding line: " + line);
  
                                  outputSet[part][filename] = line;
                                  manifest.add(path.join(localeDir, filename + ".json"));
                              }
                          } else {
                              outputSet[part][filename] = "";
                              manifest.add(path.join(localeDir, filename + ".json"));
                          }
                      } catch (e) {
                          console.log("ilib-webpack-plugin: Error: " + e);
                      }
                  }.bind(this));
              }
          });
      }.bind(this));
  
      if (charsets.size > 0) {
          var optional = new Set();
  
          if (!outputSet.root) {
              outputSet.root = {};
          }
          var data, cwdToData = path.join(dataRoot, "charsetaliases.json");
          if (!outputSet.root.charsetaliases && fs.existsSync(cwdToData)) {
              data = fs.readFileSync(cwdToData, "utf-8");
              var line = "ilib.data.charsetaliases = " + data + ";\n";
              outputSet.root.charsetaliases = line;
              manifest.add("charsetaliases.json");
          }
  
          charsets.forEach(function(charset) {
              var data, cwdToData = path.join(dataRoot, "charset", charset + ".json");
              filename = "charset_" + charset;
              if (!outputSet.root[filename] && fs.existsSync(cwdToData)) {
                  data = fs.readFileSync(cwdToData, "utf-8");
                  var line = "ilib.data.charset_" + toIlibDataName(charset) + " = " + data + ";\n";
                  outputSet.root[filename] = line;
                  manifest.add(path.join("charset", charset + ".json"));
  
                  var cs = JSON.parse(data);
                  if (typeof(cs.optional) === "boolean" && cs.optional) {
                      optional.add(charset);
                  }
              }
          });
  
          for (var locale in charmaps) {
              var loc = (locale === "*" ? "root" : locale);
              charmaps[locale].forEach(function(charset) {
                  var data, cwdToData = path.join(dataRoot, "charmaps", charset + ".json");
                  filename = "charmaps_" + charset;
                  if (!optional.has(charset) && !outputSet.root[filename] && fs.existsSync(cwdToData)) {
                      data = fs.readFileSync(cwdToData, "utf-8");
                      var line = "ilib.data.charmaps_" + toIlibDataName(charset) + " = " + data + ";\n";
                      outputSet.root[filename] = line;
                      manifest.add(path.join("charmaps", charset + ".json"));
                  }
              });
          }
      }
  
      function addForm(form, script) {
          if (script) {
              try {
                  var cwdToData = path.join(dataRoot, form, script + ".json");
                  if (fs.existsSync(cwdToData)) {
                      data = fs.readFileSync(cwdToData, "utf-8");
                      var line = '// form ' + form + ' script ' + script + '\nilib.extend(ilib.data.norm.' + form + ', ' + data + ');\n';
                      // console.log(">>>>>>>>>>>>> Adding form: " + form);
                      outputSet.root[form + "/" + script] = line;
                      manifest.add(path.join(form, script + ".json"));
                  }
              } catch (e) {
                  console.log("ilib-webpack-plugin: Error: " + e);
              }
          }
      }
  
      for (var form in normalizations) {
          if (normalizations[form].has("all")) {
              // if "all" is there, then we don't need to add each script individually
              // because they are all in the all.json already
              addForm(form, "all");
          } else {
              var set = (normalizations.size === 0 || (normalizations[form].has("") && normalizations.size === 1)) ? scripts : normalizations[form];
              set.forEach(function(script) {
                  if (options.debug) console.log("ilib-webpack-plugin: Including " + form + " for script " + script);
                  addForm(form, script);
              });
          }
      }
  
      // Write out the manifest file so that the WebpackLoader knows when to attempt
      // to load data and when not to. If a file it is attempting to load is not in
      // the manifest, it does not have to load the locale files that would contain it,
      // which leads to 404s. The ilibmanifest.json is used locally when running under
      // nodejs, and the ilibmanifest.js is used remotely when running in a browser.
      var localManifest =  {
          files: toArray(manifest)
      };
      var outputPath = path.join(outputDir, "locales"),
      outputFile = path.join(outputPath, "localmanifest.js");
      makeDirs(outputPath);
      if (options.debug) console.log("ilib-webpack-plugin: Emitting local manifest " + outputFile);
      var text = "module.exports=" + JSON.stringify(localManifest) + ";\n";
      sources[outputFile] = text;
      makeDirs(outputPath);
      fs.writeFileSync(outputFile, text, "utf-8");
  
      var remoteManifest = {
          files: Object.keys(outputSet).map(function(file) {
              return file + ".js";
          })
      };
      outputFile = path.join(outputPath, "remotemanifest.js");
      if (options.debug) console.log("ilib-webpack-plugin: Emitting remote manifest " + outputFile);
      text = "module.exports=" + JSON.stringify(remoteManifest) + ";\n";
      sources[outputFile] = text;
      fs.writeFileSync(outputFile, text, "utf-8");
  
      for (var filename in outputSet) {
          var outputFileName = filename + ".js";
          var dataFiles = outputSet[filename];
          var ilibRoot = options.ilibRoot ?
              path.join(options.ilibRoot, "lib/ilib.js") :
                  "ilib/lib/ilib.js";
  
          var output = "module.exports.installLocale = function(ilib) {\n";
  
          for (var dataFile in dataFiles) {
              output += dataFiles[dataFile];
          }
  
          output += "};\n";
  
  
          var outputFile = path.join(outputPath, outputFileName);
          if (options.debug) console.log("ilib-webpack-plugin: Emitting " + outputFile + " size " + output.length);
          // if (options.debug) console.log("ilib-webpack-plugin: Writing to " + outputFile);
          makeDirs(path.dirname(outputFile));
          fs.writeFileSync(outputFile, output, "utf-8");
          sources[outputFile] = output;   // remember this so we can update the in-memory modules later
      }
  
      // console.log("ilib-webpack-plugin: Done emitting locale data.");
      localeDataEmitted = sources;
  
      return sources;
  }