Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

new loader upgrade, first changes

  • Loading branch information...
commit 446b235837a2db347265b0feba8a098275cab39b 1 parent 9233231
@guybedford guybedford authored
View
2  LICENSE-MIT
@@ -1,4 +1,4 @@
-Copyright (c) 2012 Luke Hogan, Addy Osmani
+Copyright (c) 2013 Guy Bedford, Luke Hoban, Addy Osmani
Permission is hereby granted, free of charge, to any person
obtaining a copy of this software and associated documentation
View
10 demo/js/test1.js
@@ -1,7 +1,3 @@
-(function(){
- return{
- tester:function(){
- console.log('hello!');
- }
- }
-})();
+export function tester() {
+ console.log('hello!');
+}
View
744 lib/es6-module-loader.js
@@ -2,36 +2,65 @@
* es6-module-loader
* https://github.com/addyosmani/es6-module-loader
*
- * Copyright (c) 2012 Luke Hogan, Addy Osmani
+ * Copyright (c) 2013 Guy Bedford, Luke Hoban, Addy Osmani
* Licensed under the MIT license.
*/
(function (global) {
- // new Loader( parent [, options ] ) - Module loader constructor
- // The Loader constructor creates a new loader. The first argument is the
- // parent loader. The second is an options object
+ // new Loader( options ) - Module loader constructor
+ // The Loader constructor creates a new loader. The argument is the
+ // options object
//
// options.global - The loader's global object
- // options.baseURL - The loader's base URL
- // options.linkedTo - The source of the loader's intrinsics (not impl)
+ // options.intrinsics - The loader's intrinsic methods
// options.strict - should code evaluated in the loader be in strict mode?
- // options.resolve( relURL, baseURL ) - The URL resolution hook
- // options.fetch( relURL, baseURL, request, resolved ) - The module loading hook
- // options.translate( src, relURL, baseURL, resolved ) - source translation hook
- function Loader(parent, options) {
+ // options.normalize( request [, referer] ) - normalize hook
+ // options.resolve( normalized, { referer, metadata } ) - The URL resolution hook
+ // options.fetch( resolved, fulfill, reject, { normalized, referer, metadata } ) - The module loading hook
+ // options.translate( source, { normalized, address, metadata, type } ) - source translation hook
+ // options.link( source, { normalized, address, metadata, type } ) - the link hook
+ function Loader(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.linkedTo === null || options.linkedTo) {
- throw new Error("Setting 'linkedTo' 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 global prototype chain is:
+ // global instance (this._global) -> intrinsics (this._intrinsics) -> initial global (options.global = window)
+ // global instance is created fresh to have this chain
+ // also sets global.window = global for full global encapsulation
+
+ // Object.create(window) doesn't work...
+ var Global = function () {}
+ Global.prototype = options.global || window;
+ this._intrinsics = new Global();
+
+ // some standard intrinsics can't work through this prototype
+ // structure so we need to wrap them to allow this global
+ // abstraction layer
+ var wrapped = {};
+ var wrap = ['addEventListener', 'removeEventListener', 'getComputedStyle', 'setTimeout', 'setInterval'];
+ for (var i = 0; i < wrap.length; i++) (function (name) {
+ wrapped[name] = function () {
+ return window[name].apply(window, arguments);
+ }
+ })(wrap[i]);
+
+ this.defineBuiltins(wrapped);
+ this.defineBuiltins(options.intrinsics && options.intrinsics._intrinsics || {});
+
+ Global = function () {}
+ Global.prototype = this._intrinsics;
+ this._global = new Global();
+
+ Object.defineProperty(this._global, 'window', { value: this._global });
+
+ this._strict = !!options.strict;
+ this.normalize = options.normalize || global.System.normalize;
+ this.resolve = options.resolve || global.System.resolve;
+ this.fetch = options.fetch || global.System.fetch;
+ this.translate = options.translate || global.System.translate;
+ this.link = options.link || global.System.link;
// The internal table of module instance objects
this._mios = {};
@@ -46,102 +75,245 @@
}
});
- Object.defineProperty(Loader.prototype, "baseURL", {
- configurable: true,
- enumerable: true,
- get: function () {
- return this._baseURL;
- }
- });
-
-
- // Loader.prototype.load( url, callback, errback )
+ // Loader.prototype.load( address, callback, errback [, referer = null] )
//
// The load method takes a string representing a module URL and a
// callback that receives the result of loading, compiling, and
// executing the module at that URL. The compiled code is statically
// associated with this loader, and its URL is the given URL. The
// additional callback is used if an error occurs.
+ //
+ // load will handle import statements, but export statements are a
+ // syntax error
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 self = this;
+ if (url instanceof Array) {
+ var scriptCnt = 0;
+ for (var i = 0; i < url.length; i++) (function (i) {
+ self.load(url[i], function () {
+ scriptCnt++;
+ if (scriptCnt == url.length) {
+ callback && callback();
+ }
+ }, errback)
+ })(i);
+ return;
+ }
- var actualSrc, evalSrc;
+ if (this._mios[url]) {
+ return callback(this._mios[url]);
+ }
- actualSrc = self._translate(src, url, self._baseURL, key);
- if (self._strict) {
- actualSrc = "'use strict';\n" + actualSrc;
+
+ this.fetch(url, function (source) {
+ var opt = {
+ address: url,
+ type: 'script'
+ };
+ source = self.translate(source, opt);
+
+ self._linkExecute(url, source, opt, callback || function() {}, errback || function(err) { throw err; });
+ }, errback);
+ };
+
+ // Loader.prototype.import( name, callback, errback, referer = null )
+ // Asynchronously load a module or sequence of modules by name.
+ Loader.prototype.import = function (name, callback, errback, referer) {
+ if (name instanceof Array) {
+ var modules = [];
+ var moduleCnt = 0;
+ var self = this;
+ for (var i = 0; i < name.length; i++) (function(i) {
+ self.import(name[i], function(m) {
+ modules[i] = m;
+ moduleCnt++;
+ if (moduleCnt == name.length) {
+ callback && callback(modules);
}
+ }, errback);
+ })(i);
+ return;
+ }
+
+ name = this.normalize(name, referer);
+ var opt = {
+ referer: referer,
+ metadata: typeof name == 'object' ? name.metadata : null
+ };
+ if (typeof name != 'string') {
+ name = name.normalized;
+ }
- // add sourceURL so these are treated as script files in a debugger
- actualSrc += '\n//@ sourceURL=' + self._baseURL + url;
-
- evalSrc = eval(actualSrc);
- self.set(url, evalSrc);
- callback(self._mios[key]);
- },
- redirect: function (url, baseURL) {
- throw new Error("'redirect' not yet implemented");
- },
- reject: function (msg) {
- errback(msg);
- }
- }, key);
+ if (this._mios[name]) {
+ return callback(this._mios[name]);
+ }
+
+ this._loadNormalized(name, opt, callback, errback);
+ };
+
+ // Loader.prototype.fetch
+ // NB spec issue here - this clashes with the instance fetch function!?
+
+ // loads from a normalized module name
+ // opt has referer and metadata properties
+ Loader.prototype._loadNormalized = function (name, opt, callback, errback) {
+ callback = callback || function() {}
+
+ if (this._mios[name]) {
+ return callback(this._mios[name]);
+ }
+
+ errback = errback || function(err) {
+ throw err;
}
+
+ var self = this;
+
+ var _errback = function(err) {
+ if (!_errback.called)
+ errback(err);
+ _errback.called = true;
+ }
+ var _callback = function(module) {
+ self._mios[name] = module;
+ if (callback)
+ callback(module);
+ }
+
+ var url = this.resolve(name, opt);
+
+ if (typeof url != 'string') {
+ url = url.address;
+ // NB what to do with 'extra'?
+ }
+
+ opt.normalized = name;
+
+ this.fetch(url, function(source) {
+ opt.address = url;
+ opt.type = 'module';
+ source = self.translate(source, opt);
+ self._linkExecute(name, source, opt, _callback, _errback);
+ }, _errback, opt);
};
- // Loader.prototype.eval( src )
- // The eval method takes a string representing a Program and returns
- // the result of compiling and executing the program.
- Loader.prototype.eval = function (sourceText) {
- with(this._global) {
- eval(sourceText);
+ // given a normalized module name, the source, and the options metadata
+ // run the link and execute hooks, with the callback returning the
+ // defined module object
+ Loader.prototype._linkExecute = function (name, source, opt, callback, errback) {
+ var link = this.link(source, opt);
+ // 1. module
+ if (link instanceof Module) {
+ return callback(link);
}
+
+ // preload esprima if necessary
+ var self = this;
+ ES6Parser.loadEsprima(name, source, function() {
+ var imports, execute;
+ // 2. specified imports and execute
+ if (typeof link == 'object') {
+ imports = link.imports;
+ execute = link.execute;
+ }
+ // 3. undefined -> default
+ else
+ imports = ES6Parser.parseImports(name, source);
+
+ // stops an unnecessary load cascade
+ if (errback.called)
+ return;
+
+ var _source = source;
+ var normalizeMap = {};
+ execute = execute || function() {
+ var exports;
+ try {
+ // parses export statements and evaluates in the correct context
+ // returning the exports object
+ exports = ES6Parser.parseEval(_source, self, name, normalizeMap, opt.address);
+ }
+ catch(e) {
+ return errback(e);
+ }
+ // only return a module when not doing evalAsync
+ if (name && name.substr(0, 6) != '__eval')
+ return new Module(exports || {});
+ }
+
+ if (!imports.length)
+ return callback(execute.call(self));
+
+ var deps = [];
+ var depCnt = 0;
+ for (var i = 0; i < imports.length; i++) (function(i) {
+ // normalize
+ var referer = { name: name, address: opt.address };
+ var normalized = self.normalize(imports[i], referer);
+
+ var opt = {
+ referer: referer,
+ metadata: typeof normalized == 'object' ? normalized.metadata : null
+ };
+
+ if (typeof normalized == 'object')
+ normalized = normalized.normalized;
+
+ imports[i] = normalizeMap[imports[i]] = normalized;
+
+ self._loadNormalized(imports[i], opt, function(module) {
+ depCnt++;
+ deps[i] = module;
+ if (depCnt == imports.length) {
+ callback(execute.apply(self, deps));
+ }
+ }, errback);
+ })(i);
+
+ }, errback);
};
- // Loader.prototype.evalAsync( src, callback, errback )
- //
- // The evalAsync method takes a string representing a Program and a
- // callback that receives the result of compiling and executing the
- // program. The compiled code is statically associated with this loader,
- // and its URL is the base URL of this loader. The additional callback
- // is used if an error occurs.
-
- Loader.prototype.evalAsync = function () {
- throw new Error("'evalAsync' is not yet implemented. Its not required until module syntax is natively available.");
+ // Loader.prototype.eval( source )
+ // Synchronously executes a Script non-terminal.
+ // If the compilation process results in a fetch, a SyntaxError is thrown.
+ // The compiled code is statically associated with this loader.
+ Loader.prototype.eval = function (source) {
+ ES6Parser.parseEval(source, this);
};
+ // Loader.prototype.parseEval( source )
+ // Asynchronously executes a Script non-terminal.
+ // The compiled code is statically associated with this loader.
+ var evalCnt = 0;
+ Loader.prototype.evalAsync = function (source) {
+ // links and then evals
+ this._linkExecute('__eval' + evalCnt++, source, {}, callback, errback);
+ }
- // Loader.prototype.get( url )
+ // Loader.prototype.get ( name )
//
- // The get method looks up a module in the loader's module instance table.
- // The URL is resolved to a key by calling the loader's resolve operation.
- Loader.prototype.get = function (url) {
- var key = this._resolve(url, this._baseURL);
- return this._mios[key];
+ // Look up a module in the loader’s registry, using a name that is assumed
+ // to be normalized.
+ Loader.prototype.get = function (name) {
+ return this._mios[name] || null;
};
- // Loader.prototype.set( urlOrMods[, mod ] )
+ // Loader.prototype.set( name, mod )
//
- // The set method stores a module or set of modules in the loader's
- // module instance table. Each URL is resolved to a key by calling
- // the loader's resolve operation.
- 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]);
- }
- }
+ // Stores (possibly overwriting) a module instance object
+ // in the loader’s registry, using a name that is assumed to be normalized.
+ Loader.prototype.set = function (name, mod) {
+ this._mios[name] = Module(mod);
+ };
+
+ Loader.prototype.has = function (name) {
+ return !!this._mios[name];
+ };
+
+ Loader.prototype.delete = function (name) {
+ delete this._mios[name];
};
// Loader.prototype.defineBuiltins( [ obj ] )
@@ -150,11 +322,11 @@
// objects and functions of the ES6 standard library associated with this
// loader's intrinsics as properties on the object.
Loader.prototype.defineBuiltins = function (o) {
- if (typeof o != "object") throw new Error("Expected object");
- for (var globalProp in global) {
- o[globalProp] = global;
+ for (var p in o) {
+ if (o.hasOwnProperty(p)) {
+ this._intrinsics[p] = o[p];
+ }
}
- return o;
};
@@ -186,34 +358,394 @@
// Pre-configured Loader instance for easier use
- var defaultSystemLoader = new Loader(null, {
+ var absUrlRegEx = /^\/|([^\:\/]*:)/;
+ var isUrl = function(name) {
+ return name.substr(name.length - 3, 3) == '.js' || name.match(absUrlRegEx);
+ }
+ var defaultSystemLoader = new Loader({
global: window,
- baseURL: document.URL.substring(0, document.URL.lastIndexOf('\/') + 1),
strict: false,
- resolve: function (relURL, baseURL) {
- var url = baseURL + relURL;
- return url;
+ normalize: function(name, referer) {
+ if (isUrl(name))
+ return name;
+ var parentName = referer && referer.name;
+ if (name.substr(0, 2) == './') {
+ var parentParts = parentName.split('/');
+ if (!parentParts.length)
+ return name.substr(2);
+ parentParts.pop();
+ parentParts.push(name.substr(2));
+ return parentParts.join('/');
+ }
+ if (name.substr(0, 3) == '../') {
+ var parentParts = parentName.split('/');
+ if (!parentParts.length)
+ throw "Path below baseUrl";
+ parentParts.pop();
+ return this.normalize(name.substr(3), { name: parentParts.join('/') });
+ }
+ return name;
+ },
+ resolve: function (name, options) {
+ for (var r in this.ondemandTable)
+ if (this.ondemandTable[r].indexOf(name) != -1)
+ return name;
+ if (isUrl(name))
+ return name;
+ return this.baseURL + (this.baseURL.substr(this.baseURL.length - 1, 1) != '/' ? '/' : '') + name + (name.split('/').pop().indexOf('.') == -1 ? '.js' : '');
},
- fetch: function (relURL, baseURL, request, resolved) {
- var url = baseURL + relURL;
+ fetch: function (url, fulfill, reject, options) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === 4) {
- if (xhr.status === 200) {
- request.fulfill(xhr.responseText);
+ if (xhr.status === 200 || (xhr.status == 0 && xhr.responseText)) {
+ fulfill(xhr.responseText);
} else {
- request.reject(xhr.statusText);
+ reject(xhr.statusText);
}
}
};
xhr.open("GET", url, true);
xhr.send(null);
},
- translate: function (src, relURL, baseURL, resolved) {
- return src;
- }
+ translate: function (source, options) {
+ return source;
+ },
+ link: function (source, options) {}
});
+ defaultSystemLoader.baseURL = document.URL.substring(0, window.location.href.lastIndexOf('\/') + 1);
+ defaultSystemLoader.ondemandTable = {};
+ defaultSystemLoader.ondemand = function (ondemandTable) {
+ for (var r in ondemandTable) {
+ this.ondemandTable[r] = this.ondemandTable[r] || [];
+ if (ondemandTable[r] instanceof Array)
+ this.ondemandTable[r] = this.ondemandTable[r].concat(ondemandTable[r]);
+ else
+ this.ondemandTable[r].push(ondemandTable[r]);
+ }
+ }
+
+
+
+
+ // Syntax Parsing and Source Modifying Polyfills
+
+ // esprima-based parser for module syntax, with pluggable polyfill support
+ var scripts = document.getElementsByTagName('script');
+ var curPath = scripts[scripts.length - 1].src;
+ var basePath = curPath.substr(0, curPath.lastIndexOf('/') + 1);
+ var ES6Parser = {
+ traverse: function(object, iterator) {
+ var key, child;
+ if (iterator(object) === false)
+ return;
+ for (key in object) {
+ child = object[key];
+ if (typeof child == 'object' && child !== null)
+ this.traverse(child, iterator);
+ }
+ },
+ // module syntax regexs - may over-classify but not under-classify
+ // simply designed as a first level check to catch any use of
+ // module syntax, before loading esprima for deeper analysis
+ importRegEx: /^\s*import\s+./m,
+ exportRegEx: /^\s*export\s+(\{|\*|var|class|function|default)/m,
+ moduleRegEx: /^\s*module\s+("[^"]+"|'[^']+')\s*\{/m,
+ checkModuleSyntax: function(name, source) {
+ if (name == null || this.parseNames[name] === undefined)
+ this.parseNames[name] = source && !!(source.match(this.importRegEx) || source.match(this.exportRegEx) || source.match(this.moduleRegEx));
+ return this.parseNames[name];
+ },
+ loadEsprima: function(name, source, callback, errback) {
+ // use a regex to check if the source contains 'import', 'export' or 'module' statements
+ // may incorrectly fire, but the damage is only an http request to do better parsing shortly
+ if (!this.checkModuleSyntax(name, source))
+ return callback();
+ var self = this;
+ System.load(basePath + 'esprima-es6.min.js', function() {
+ self.esprima = System.global.esprima;
+ callback();
+ });
+ },
+ parseNames: {},
+ treeCache: {},
+ parseImports: function(name, source) {
+ // regex showed no need for esprima -> return empty
+ if (!this.checkModuleSyntax(name, source))
+ return [];
+
+ var tree = this.treeCache[name] || (this.treeCache[name] = this.esprima.parse(source, { range: true }));
+ var imports = [];
+ this.traverse(tree, function(node) {
+ if (node.type == 'ImportDeclaration') {
+ var moduleName;
+ // import 'jquery' as $;
+ if (node.from.type == 'Literal')
+ moduleName = node.from.value;
+ // import * from foo;
+ else if (node.from.type == 'Path')
+ moduleName = node.from.body[0].name;
+ imports.push(moduleName);
+ }
+ else if (node.type == 'ExportDeclaration') {
+ // export ... from blah
+ // export ... from 'blah'
+ if (node.specifiers && node.specifiers[0] && node.specifiers[0].from) {
+ if (node.specifiers[0].from.body == 'Path')
+ imports.push(node.specifiers[0].from.body[0].name);
+ else
+ imports.push(node.specifiers[0].from.value);
+ }
+ }
+ });
+ return imports;
+ },
+ addPolyfill: function(polyfill, callback, errback) {
+ // by virtue of adding a polyfill, we now load esprima by default
+ this.loadEsprima(null, null, callback, errback);
+ this.polyfills.push(polyfill);
+ },
+ polyfills: [],
+ applyPolyfill: function(node, tSource) {
+ for (var i = 0; i < this.polyfills.length; i++)
+ this.polyfills[i](node, tSource);
+ },
+
+ // runs an eval of code with module syntax
+ // name, normalize optional
+ parseEval: function(source, loader, name, normalizeMap, sourceURL) {
+
+ // regex showed no need for esprima - normal eval
+ if (!this.checkModuleSyntax(name, source)) {
+ var __Loader = loader;
+ eval('(function(window) { with(__Loader.global) { ' + (loader._strict ? '"use strict";\n' : '') + source + ' } }).call(__Loader.global, __Loader.global); ' + (sourceURL ? '\n//# sourceURL=' + sourceURL : ''));
+ return;
+ }
+
+ var tree = this.treeCache[name] || this.esprima.parse(source, { range: true });
+
+ normalizeMap = normalizeMap || {};
+
+ var tSource = new SourceModifier(source);
+
+ var self = this;
+ this.traverse(tree, function(node) {
+
+ // --- Imports ---
+ // replaces imports with Loader.get
+ // https://github.com/ariya/esprima/blob/harmony/test/harmonytest.js#L4067
+
+ if (node.type == 'ImportDeclaration') {
+ // import 'jquery' as $;
+ if (node.from.type == 'Literal' && node.specifiers[0].type != 'ImportSpecifier') {
+ tSource.replace(node.range[0], node.range[1],
+ "var " + node.as.name + " = __Loader.get('" + (normalizeMap[node.from.value] || node.from.value) + "');");
+ }
+
+ // import ... from foo
+ else {
+ var moduleName;
+ if (node.from.body)
+ moduleName = node.from.body[0].name;
+ else if (node.from.type == 'Literal')
+ moduleName = node.from.value;
+
+ moduleName = normalizeMap[moduleName] || moduleName;
+
+ // import * from foo;
+ if (node.specifiers[0].type == 'Glob')
+ tSource.replace(node.range[0], node.range[1],
+ "var __module = __Loader.get('" + moduleName + "');" +
+ "for (var m in __module)" +
+ " __global[m] = __module[m];");
+
+ // import { a } from foo;
+ // import { a: mapping, of: imports } from foo
+ else if (node.specifiers[0].type == 'ImportSpecifier') {
+ var replaceSource = "var __module = __Loader.get('" + moduleName + "');";
+ for (var i = 0; i < node.specifiers.length; i++ ) {
+ replaceSource += "var " + node.specifiers[i].id.name + " = " +
+ "__module['" + (node.specifiers[i].from ? node.specifiers[i].from.body[0].name : node.specifiers[i].id.name) + "'];";
+ }
+ tSource.replace(node.range[0], node.range[1], replaceSource);
+ }
+ }
+ }
+
+ // --- Exports ---
+ // NB throw an error for exports being present when (name && name.substr(0, 6) != '__eval')
+ // replace exports with __exports.export = ...
+ // https://github.com/ariya/esprima/blob/harmony/test/harmonytest.js#L3288
+
+ // exports = ...
+ else if (node.type == 'AssignmentExpression' && node.left.name && node.left.name == 'exports')
+ tSource.replace(node.left.range[0], node.left.range[1], '__exports');
+
+ else if (node.type == 'ExportDeclaration') {
+ var fromModule;
+ var exports;
+ // export ... from blah
+ // export ... from 'blah'
+ if (node.specifiers && node.specifiers[0] && node.specifiers[0].from) {
+ if (node.specifiers[0].from.body == 'Path')
+ fromModule = node.specifiers[0].from.body[0].name;
+ else
+ fromModule = node.specifiers[0].from.value;
+ }
+ if (!node.declaration && node.specifiers) {
+ // export *
+ if (node.specifiers[0].id.type == 'Glob')
+ exports = true;
+
+ // export { A: some.thing, B: another.thing, C }
+ else if (node.specifiers[0].type == 'ExportSpecifierSet') {
+ exports = {};
+ for (var i = 0; i < node.specifiers[0].specifiers.length; i++) {
+ if (node.specifiers[0].specifiers[i].from)
+ exports[node.specifiers[0].specifiers[i].id.name] = tSource.getRange.apply(tSource, node.specifiers[0].specifiers[i].from.range);
+ else
+ exports[node.specifiers[0].specifiers[i].id.name] = node.specifiers[0].specifiers[i].id.name;
+ }
+ }
+ // export varA, varB, varC
+ else if (node.specifiers[0].type = 'ExportSpecifier') {
+ exports = {};
+ for (var i = 0; i < node.specifiers.length; i++)
+ exports[node.specifiers[i].id.name] = node.specifiers[i].id.name;
+ }
+ }
+ // export var p
+ else if (node.declaration.type == 'VariableDeclaration')
+ exports = node.declaration.declarations[0].id.name;
+ // export function p() {}
+ else if (node.declaration.type == 'FunctionDeclaration')
+ exports = node.declaration.id.name;
+
+ // export module p {}
+ // export class p {}
+ else if (node.declaration.type == 'ModuleDeclaration' || node.declaration.type == 'ClassDeclaration')
+ exports = node.declaration.declarations[0].id.name;
+
+ if (fromModule) {
+ fromModule = normalize(fromModule);
+ // export * from someModule
+ if (exports === true)
+ tSource.replace(node.range[0], node.range[1], "__exports = __Loader.get('" + fromModule + "');");
+ // export { some: var } from someModule
+ else {
+ var replaceSource = "";
+ for (var e in exports)
+ replaceSource += "__exports['" + e + "'] = __Loader.get('" + fromModule + "')['" + exports[e] + "'];";
+ tSource.replace(node.range[0], node.range[1], replaceSource);
+ }
+ }
+ else if (typeof exports == 'object') {
+ // export { a, b, c: d }
+ var replaceSource = "";
+ for (var e in exports)
+ replaceSource += "__exports['" + e + "'] = " + exports[e] + "; ";
+ tSource.replace(node.range[0], node.range[1], replaceSource);
+ }
+ else if (typeof exports == 'string') {
+ // export var p = 5 etc
+ if (node.declaration.declarations)
+ tSource.replace(node.range[0], node.declaration.declarations[0].id.range[0] - 1, "__exports['" + exports + "'] = ");
+ // export function q() { ... }
+ else if (node.declaration.type == 'FunctionDeclaration')
+ tSource.replace(node.range[0], node.declaration.range[0] - 1, "__exports['" + exports + "'] = ");
+
+ else
+ tSource.replace(node.range[0], node.declaration.id.range[0] - 1, "__exports['" + exports + "'] = ");
+ }
+ }
+
+ // --- Modules ---
+ // replaces modules with Loader.set
+
+ else if (node.type == 'ModuleDeclaration') {
+ var moduleName = node.id.name;
+ // module foo { ..code.. }
+ // -> (function() { var __exports = {}; ..code.. __Loader.set("foo", new Module(__exports)); })();
+ if (node.body.type == 'BlockStatement') {
+ tSource.replace(node.range[0], node.body.range[0] + 1, "(function() { var __exports = {}; ");
+ tSource.replace(node.body.range[1], node.range[1], " __Loader.set('" + moduleName + "', new Module(__exports)); })();");
+ }
+ // module names are assumed to be normalized already
+ // so nested modules not currently supported
+ }
+
+ // --- Polyfills ---
+
+ else if (self.polyfills.length)
+ self.applyPolyfill(node, tSource);
+ });
+
+ delete this.treeCache[name];
+
+ var __Loader = loader;
+ var evalSource = '(function(window) { with(__Loader.global) { ' + (loader._strict ? '"use strict";\n' : '') + tSource.toString() + ' } }).call(__Loader.global, __Loader.global);' + (sourceURL ? '\n//# sourceURL=' + sourceURL : '');
+ var output = eval(evalSource);
+
+ return output;
+ }
+ };
+
+ /*
+ * SourceModifier
+ *
+ * Allows for partial modification of a source file based on successive
+ * range adjustment operations consistent with the original source file
+ *
+ * Example:
+ * 012345678910
+ * var h = new SourceModifier('hello world');
+ * h.replace(2, 4, 'y');
+ * h.replace(6, 10, 'person');
+ * h.source == 'hey person';
+ * h.rangeOps == [{start: 2, end: 4, diff: -2}, {start: 4, end: 9, diff: 1}]
+ *
+ */
+ var SourceModifier = function(source) {
+ this.source = source;
+ this.rangeOps = [];
+ }
+ SourceModifier.prototype = {
+ mapIndex: function(index) {
+ // apply the range operations in order to the index
+ for (var i = 0; i < this.rangeOps.length; i++) {
+ var curOp = this.rangeOps[i];
+ if (curOp.start >= index)
+ continue;
+ if (curOp.end <= index) {
+ index += curOp.diff;
+ continue;
+ }
+ throw 'Source location ' + index + ' has already been transformed!';
+ }
+ return index;
+ },
+ replace: function(start, end, replacement) {
+ var diff = replacement.length - (end - start + 1);
+
+ start = this.mapIndex(start);
+ end = this.mapIndex(end);
+
+ this.source = this.source.substr(0, start) + replacement + this.source.substr(end + 1);
+
+ this.rangeOps.push({
+ start: start,
+ end: end,
+ diff: diff
+ });
+ },
+ getRange: function(start, end) {
+ return this.source.substr(this.mapIndex(start), this.mapIndex(end));
+ },
+ toString: function() {
+ return this.source;
+ }
+ };
// Export the Loader class
global.Loader = Loader;
View
4 package.json
@@ -1,10 +1,10 @@
{
"name": "es6-module-loader",
"description": "An ES6 Module Loader shim",
- "version": "0.1.0",
+ "version": "0.2.0",
"homepage": "https://github.com/addyosmani/es6-module-loader",
"author": {
- "name": "Luke Hogan, Addy Osmani",
+ "name": "Guy Bedford, Luke Hoban, Addy Osmani",
"email": "addyosmani@gmail.com",
"url": "none"
},
Please sign in to comment.
Something went wrong with that request. Please try again.