public
Last active

ES6 module loader API polyfill experiment

  • Download Gist
gistfile1.js
JavaScript
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
(function (global) {
 
// TODO: Delegate to parent loader in various places
// TODO: Canonicalization
// TODO: Can we do better for 'eval'?
 
/// Module loader constructor
function Loader(parent, options) {
 
// Initialization of loader state from options
this._global = options.global || Object.create(null);
this._baseURL = options.baseURL || this.global && this.global.baseURL;
if (options.intrinsics === null || options.intrinsics) {
throw new Error("Setting 'intrinsics' not yet supported.");
}
this._strict = options.string === undefined ? false : !!options.string;
this._resolve = options.resolve || parent.resolve;
this._fetch = options.fetch || parent.fetch;
this._translate = options.translate || parent.translate;
 
// The internal table of module instance objects
this._mios = {}
}
 
Object.defineProperty(Loader.prototype, "global", { configurable: true, enumerable: true, get: function () {
return this._global;
}});
 
Object.defineProperty(Loader.prototype, "baseURL", { configurable: true, enumerable: true, get: function () {
return this._baseURL;
}});
 
Loader.prototype.load = function (url, callback, errback) {
var key = this._resolve(url, this._baseURL);
if (this._mios[key]) {
callback(this._mios[key]);
} else {
var self = this;
this._fetch(url, this._baseURL, {
fulfill: function (src) {
var actualSrc = self._translate(src, url, self._baseURL, key);
if (self._strict) {
actualSrc = "'use strict';\n" + actualSrc;
}
eval(actualSrc);
callback(self._mios[key]);
},
redirect: function (url, baseURL) {
throw new Error("'redirect' not yet implemented");
},
reject: function (msg) {
errback(msg);
}
}, key);
}
}
 
Loader.prototype.eval = function (sourceText) {
with (this._global) {
eval(sourceText);
}
}
 
Loader.prototype.evalAsync = function () {
throw new Error("'evalAsync' not yet implemented. Not needed until module syntax is available.");
}
 
Loader.prototype.get = function (url) {
var key = this._resolve(url, this._baseURL);
return this._mios[key];
}
 
Loader.prototype.set = function (url, mio) {
var key = this._resolve(url, this._baseURL);
if (typeof url == "string") {
this._mios[key] = Module(mio);
} else {
for (var p in key) {
this._mios[p] = Module(key[p]);
}
}
}
 
Loader.prototype.defineBuiltins = function (o) {
if (typeof o != "object") throw new Error("Expected object");
for (var globalProp in global) {
o[globalProp] = global;
}
return o;
}
 
function Module(o) {
if (o == null) throw new TypeError("Expected object");
var obj = Object(o);
if (obj instanceof Module) {
return obj;
} else {
var mio = Object.create(null);
for (var key in obj) {
(function(key) {
Object.defineProperty(mio, key, { configurable: false, enumerable: true, get: function () {
return obj[key];
}});
})(key);
}
return mio;
}
}
 
var defaultSystemLoader = new Loader(null, {
global: window,
baseURL: document.URL.substring(0, document.URL.lastIndexOf('\/') + 1),
strict: false,
resolve: function (relURL, baseURL) {
var url = baseURL + relURL;
return url;
},
fetch: function (relURL, baseURL, request, resolved) {
var url = baseURL + relURL;
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
request.fulfill(xhr.responseText);
} else {
request.reject(xhr.statusText);
}
}
};
xhr.open("GET", url, true);
xhr.send(null);
},
translate: function (src, relURL, baseURL, resolved) {
return src;
}
});
 
// Export the Loader class
global.Loader = Loader;
 
// Export the Module class
global.Module = Module;
 
// Export the System object
global.System = defaultSystemLoader;
 
})(this);

Awesome, I really like this polyfill approach. Any plans to take it further?

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.