1 /* 2 * WebpackLoader.js - Loader implementation for webpack'ed ilib on the web 3 * 4 * Copyright © 2018, JEDLSoft 5 * 6 * Licensed under the Apache License, Version 2.0 (the "License"); 7 * you may not use this file except in compliance with the License. 8 * You may obtain a copy of the License at 9 * 10 * http://www.apache.org/licenses/LICENSE-2.0 11 * 12 * Unless required by applicable law or agreed to in writing, software 13 * distributed under the License is distributed on an "AS IS" BASIS, 14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 15 * 16 * See the License for the specific language governing permissions and 17 * limitations under the License. 18 */ 19 20 var Loader = require("./Loader.js"); 21 var Path = require("./Path.js"); 22 var Locale = require("./Locale.js"); 23 var ISet = require("./ISet.js"); 24 var Utils = require("./Utils.js"); 25 var JSUtils = require("./JSUtils.js"); 26 27 var alreadyLoaded = new ISet(); 28 29 function loadLocaleData(ilib, locale, callback) { 30 switch (locale) { 31 // This special macro will get replaced in the ilibdata-webpack-loader with 32 // case statements for each locale js file so that webpack can make separate 33 // bundles for each one of those files when in dynamic load mode. In static 34 // (assembled) mode, this will get replaced with require calls that cause 35 // the data to be put into one giant webpack bundle. 36 37 // !loadLocaleData 38 39 case "dummy": 40 // This is just here to prevent webpack from removing the function contents. Otherwise, 41 // it will think this whole function is "unused" or "dead" code. 42 console.log("Should never happen"); 43 callback(); 44 break; 45 } 46 } 47 48 /** 49 * @class 50 * Implementation of Loader for webpack. 51 * 52 * @constructor 53 * @private 54 */ 55 module.exports = function (ilib) { 56 var WebpackLoader = function (ilib) { 57 // util.print("new common WebpackLoader instance\n"); 58 59 this.parent.call(this, ilib); 60 this.ilib = ilib; 61 this.manifests = {}; 62 63 this.includePath.push(""); 64 }; 65 66 // make this a subclass of loader 67 WebpackLoader.prototype = new Loader(); 68 WebpackLoader.prototype.parent = Loader; 69 WebpackLoader.prototype.constructor = WebpackLoader; 70 71 WebpackLoader.prototype.name = "WebpackLoader"; 72 73 WebpackLoader.prototype.getData = function(dataName, tzName) { 74 return dataName === "zoneinfo" ? this.ilib.data.zoneinfo[tzName] : this.ilib.data[dataName]; 75 }; 76 77 // used locally with node 78 WebpackLoader.prototype._loadFile = function (pathname, sync, cb) { 79 var dir = Path.dirname(pathname); 80 var base = Path.basename(pathname, "json"); 81 var dataName = base; 82 var locale = undefined; 83 var filename; 84 var tzName; 85 86 if (dir) { 87 var locpath = /(^|\/)([a-z][a-z][a-z]?(\/[A-Z][a-z][a-z][a-z])?(\/[A-Z][A-Z](\/[A-Z]+)?)?)$/.exec(dir); 88 if (locpath) { 89 locale = new Locale(locpath[2].replace(/\//g, "-")); 90 } 91 } 92 93 filename = locale && locale.getSpec() || (base === "ilibmanifest" ? "localmanifest" : "root"); 94 95 var dataName = base; 96 if (dir) { 97 if (locale) { 98 dataName += "_" + filename; 99 } else if (dir === "charset" || dir === "charmaps") { 100 dataName = dir + "_" + base; 101 } else if (dir.substring(0, 9) === "zoneinfo/") { 102 dataName = "zoneinfo"; 103 tzName = pathname.substring(9).substring(0, - 5); 104 } 105 } 106 107 dataName = dataName.replace(/[\.:\(\)\/\\\+\-]/g, "_"); 108 109 var data = this.getData(dataName, tzName); 110 111 if (data) { 112 // already loaded 113 if (typeof(cb) === "function") cb(data); 114 } else if (!alreadyLoaded.has(filename)) { 115 console.log("WebpackLoader._loadFile: loading " + pathname + (sync ? " sync" : " async") + " as " + filename + ".js"); 116 117 alreadyLoaded.add(filename); 118 loadLocaleData(this.ilib, filename, function(callback, data) { 119 data = (data && typeof(data) === "object" && typeof(data.installLocale) === "function") ? this.getData(dataName, tzName) : data; 120 if (callback && typeof(callback) === "function") { 121 callback(data); 122 } 123 }.bind(this, cb)); 124 } else { 125 if (typeof(cb) === "function") cb(); 126 } 127 128 // return data; 129 }; 130 131 WebpackLoader.prototype._exists = function(dir, file) { 132 return false; 133 }; 134 135 // used remotely on a browser 136 WebpackLoader.prototype._loadLocaleFile = function(path, sync, callback) { 137 var base = Path.basename(path, ".js"); 138 139 if (alreadyLoaded.has(base)) { 140 callback(); 141 } else { 142 console.log("WebpackLoader._loadLocaleFile: loading " + path + (sync ? " sync" : " async")); 143 144 alreadyLoaded.add(base); 145 loadLocaleData(this.ilib, base, function(callback, data) { 146 callback(data); 147 }.bind(this, callback)); 148 } 149 }; 150 151 /** 152 * @private 153 */ 154 WebpackLoader.prototype._ensureManifest = function(locale, dir, callback) { 155 if (this.manifests[dir]) { 156 callback(this.manifests[dir]); 157 } else { 158 this._loadLocaleFile(Path.join(dir, "remotemanifest.js"), false, ilib.bind(this, function(manifest) { 159 if (manifest) { 160 this.manifests[dir] = manifest.files; 161 callback(manifest.files); 162 } else { 163 callback(); // undefined param indicates error 164 } 165 })); 166 } 167 }; 168 169 /** 170 * Ensure that the data for a locale is loaded into memory from the given 171 * dir. This will decompose the locale into its constituent parts and 172 * load all the appropriate files based on those parts. Thereafter, 173 * all ilib code can be called synchronously. 174 */ 175 WebpackLoader.prototype.ensureLocale = function(locale, dir, callback) { 176 this._ensureManifest(locale, dir, ilib.bind(this, function(manifest) { 177 var filesToLoad = Utils.getSublocales(locale).map(function(sublocale) { 178 return sublocale + ".js"; 179 }).filter(function(file) { 180 return !manifest || Loader.indexOf(manifest, file) > -1; 181 }); 182 JSUtils.callAll(filesToLoad, ilib.bind(this, function(arr, cb) { 183 this._loadLocaleFile(arr[0], false, cb); 184 }), function(results) { 185 callback(results); 186 }); 187 })); 188 }; 189 190 return new WebpackLoader(ilib); 191 }; 192