Skip to content

Instantly share code, notes, and snippets.

@guybedford
Last active September 27, 2015 15:17
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save guybedford/11164189 to your computer and use it in GitHub Desktop.
Save guybedford/11164189 to your computer and use it in GitHub Desktop.
System.register ES6 form loader hooks implementation for https://github.com/google/traceur-compiler/issues/953#issuecomment-40969512
// our side table
// registry = { deps, declare ; normalizedDeps, depMap ; execute, exports }
// Lifecycle:
// 1. before instantiate, just deps, declare
// 2. after instantiate, normalizedDeps and depMap
// 3. after linking, execute, exports
// 4. after partial tree execution just exports
// 5. after full tree execution, removed from registry
var registry = {};
loader.register = function(name, deps, declare) {
registry[name] = {
deps: deps,
declare: declare
};
}
// recursively ensure that a module and all its dependencies are declared
// that is either in the loader registry or have registry.exports in the
// side table registry
// returns the exports object for convenience in recursion
function ensureDeclared(moduleName) {
var registryEntry = registry[moduleName];
// no registry entry -> already executed or ES6.
// Run a System.get for ES6 modules to work with loader.register
// (a complete edge case, but makes it consistent)
if (!registryEntry)
return System.get(moduleName);
// exports already declared
if (registryEntry.exports)
return registryEntry.exports;
// not yet declared
// -> declare this module
// -> then declare its dependencies, populating the depMap
// top-down order avoids issues of cirularity
var declared = registryEntry.declare(registryEntry.depMap);
registryEntry.exports = declared.exports;
registryEntry.execute = declared.execute;
for (var i = 0; i < registryEntry.normalizedDeps.length; i++) {
var depName = registryEntry.normalizedDeps[i];
registryEntry.depMap[registryEntry.deps[i]] = ensureDeclared(depName);
}
return registryEntry.exports;
}
// given a module, and the list of modules for this current branch,
// ensure that each of the dependencies of this module is evaluated
// (unless one is a circular dependency already in the list of seen
// modules, in which case we execute it)
// then evaluate the module itself
// depth-first left to right execution to match ES6 modules
function ensureEvaluated(moduleName, seen) {
var registryEntry = registry[moduleName];
// if already seen, that means it's an already-evaluated non circular dependency
if (seen.indexOf(moduleName) != -1)
return;
seen.push(moduleName);
for (var i = 0; i < registryEntry.normalizedDeps.length; i++) {
var depName = registryEntry.normalizedDeps[i];
// circular -> execute now if not already executed
if (seen.indexOf(depName) != -1) {
var depEntry = registry[depName];
if (depEntry && depEntry.execute)
depEntry.execute.call(loader.global);
delete depEntry.execute;
}
// in turn ensure dependencies are evaluated
else
ensureEvaluated(depName, seen);
}
// we've evaluated all dependencies so evaluate this module now
registryEntry.execute.call(loader.global);
}
var loaderInstantiate = loader.instantiate;
loader.instantiate = function(load) {
var registryEntry = registry[load.name];
if (!registryEntry)
return loaderInstantiate.call(this, load);
// first, normalize all dependencies
var normalizePromises = [];
for (var i = 0; i < deps.length; i++)
normalizePromises.push(Promise.resolve(loader.normalize(deps[i], load.name)));
return Promise.all(normalizePromises).then(function(normalizedDeps) {
registryEntry.normalizedDeps = normalizedDeps;
// create the empty dep map - this is our key deferred dependency binding object passed into declare
registryEntry.depMap = {};
return {
deps: registryEntry.deps,
execute: function() {
// recursively ensure that the module and all its dependencies are declared
ensureDeclared(load.name);
// now handle dependency execution in correct order
ensureEvaluated(load.name, []);
// remove from the registry
delete registry[load.name];
// return the defined module object
return Module(registryEntry.exports);
}
};
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment