es-module-loader
Advanced tools
Comparing version 1.2.1 to 1.3.0
@@ -675,20 +675,36 @@ import RegisterLoader from '../core/register-loader.js'; | ||
suite.add('Importing mulitple trees at the same time with RegisterLoader', async function() { | ||
var loader = declaredRegisterLoader(); | ||
await Promise.all(allModules.map(m => loader.import(m))); | ||
suite.add('Importing multiple trees at the same time with RegisterLoader', { | ||
defer: true, | ||
fn: async function(deferred) { | ||
var loader = declaredRegisterLoader(); | ||
await Promise.all(allModules.map(m => loader.import(m))); | ||
deferred.resolve(); | ||
} | ||
}); | ||
suite.add('Importing a deep tree of modules with RegisterLoader', async function() { | ||
var loader = declaredRegisterLoader(); | ||
await loader.import('_a.js'); | ||
suite.add('Importing a deep tree of modules with RegisterLoader', { | ||
defer: true, | ||
fn: async function(deferred) { | ||
var loader = declaredRegisterLoader(); | ||
await loader.import('_a.js'); | ||
deferred.resolve(); | ||
} | ||
}); | ||
suite.add('Importing a module with deps with RegisterLoader', async function() { | ||
var loader = declaredRegisterLoader(); | ||
await loader.import('es6-withdep.js'); | ||
suite.add('Importing a module with deps with RegisterLoader', { | ||
defer: true, | ||
fn: async function(deferred) { | ||
var loader = declaredRegisterLoader(); | ||
await loader.import('es6-withdep.js'); | ||
deferred.resolve(); | ||
} | ||
}); | ||
suite.add('Importing a single registered module with RegisterLoader', async function() { | ||
var loader = declaredRegisterLoader(); | ||
await loader.import('no-imports.js'); | ||
suite.add('Importing a single registered module with RegisterLoader', { | ||
defer: true, | ||
fn: async function(deferred) { | ||
var loader = declaredRegisterLoader(); | ||
await loader.import('no-imports.js'); | ||
deferred.resolve(); | ||
} | ||
}); | ||
@@ -723,20 +739,36 @@ | ||
suite.add('Importing mulitple trees at the same time with SystemJS', async function() { | ||
var loader = declaredSystemJSLoader(); | ||
await Promise.all(allModules.map(m => loader.import(m))); | ||
suite.add('Importing multiple trees at the same time with SystemJS', { | ||
defer: true, | ||
fn: async function(deferred) { | ||
var loader = declaredSystemJSLoader(); | ||
await Promise.all(allModules.map(m => loader.import(m))); | ||
deferred.resolve(); | ||
} | ||
}); | ||
suite.add('Importing a single registered module with SystemJS', async function() { | ||
var loader = declaredSystemJSLoader(); | ||
await loader.import('no-imports.js'); | ||
suite.add('Importing a deep tree of modules with SystemJS', { | ||
defer: true, | ||
fn: async function(deferred) { | ||
var loader = declaredSystemJSLoader(); | ||
await loader.import('_a.js'); | ||
deferred.resolve(); | ||
} | ||
}); | ||
suite.add('Importing a module with deps with SystemJS', async function() { | ||
var loader = declaredSystemJSLoader(); | ||
await loader.import('es6-withdep.js'); | ||
suite.add('Importing a module with deps with SystemJS', { | ||
defer: true, | ||
fn: async function(deferred) { | ||
var loader = declaredSystemJSLoader(); | ||
await loader.import('es6-withdep.js'); | ||
deferred.resolve(); | ||
} | ||
}); | ||
suite.add('Importing a deep tree of modules with SystemJS', async function() { | ||
var loader = declaredSystemJSLoader(); | ||
await loader.import('_a.js'); | ||
suite.add('Importing a single registered module with SystemJS', { | ||
defer: true, | ||
fn: async function(deferred) { | ||
var loader = declaredSystemJSLoader(); | ||
await loader.import('no-imports.js'); | ||
deferred.resolve(); | ||
} | ||
}); |
@@ -9,2 +9,8 @@ import fs from 'fs'; | ||
process.on('unhandledRejection', function (e) { | ||
setTimeout(function () { | ||
throw e; | ||
}); | ||
}); | ||
function runNextBenchmark() { | ||
@@ -34,3 +40,3 @@ var nextBenchmark = benchmarks.shift(); | ||
suite.run({ async: true, maxTime: 1, minSamples: 1 }); | ||
suite.run({ defer: true }); | ||
}); | ||
@@ -43,2 +49,2 @@ }) | ||
runNextBenchmark(); | ||
runNextBenchmark(); |
@@ -11,7 +11,15 @@ /* | ||
export function pathToFileUrl(filePath) { | ||
/* | ||
* Simple Symbol() shim | ||
*/ | ||
var hasSymbol = typeof Symbol !== 'undefined'; | ||
export function createSymbol (name) { | ||
return hasSymbol ? Symbol() : '@@' + name; | ||
} | ||
export function pathToFileUrl (filePath) { | ||
return 'file://' + (isWindows ? '/' : '') + filePath; | ||
} | ||
export function fileUrlToPath(fileUrl) { | ||
export function fileUrlToPath (fileUrl) { | ||
if (fileUrl.substr(0, 7) !== 'file://') | ||
@@ -65,3 +73,3 @@ throw new RangeError(fileUrl + ' is not a valid file url'); | ||
var errArgs = new Error(0, '_').fileName == '_'; | ||
function LoaderError__Check_error_message_above_for_loader_stack(childErr, newMessage) { | ||
function LoaderError__Check_error_message_for_loader_stack (childErr, newMessage) { | ||
// Convert file:/// URLs to paths in Node | ||
@@ -92,2 +100,2 @@ if (!isBrowser) | ||
} | ||
export { LoaderError__Check_error_message_above_for_loader_stack as addToError } | ||
export { LoaderError__Check_error_message_for_loader_stack as addToError } |
@@ -1,11 +0,6 @@ | ||
import { baseURI, addToError } from './common.js'; | ||
export { Loader, Module, ModuleNamespace as InternalModuleNamespace } | ||
import { baseURI, addToError, createSymbol } from './common.js'; | ||
/* | ||
* Simple Symbol() shim | ||
*/ | ||
var hasSymbol = typeof Symbol !== 'undefined'; | ||
function createSymbol(name) { | ||
return hasSymbol ? Symbol() : '@@' + name; | ||
} | ||
// multiple exports of Module for backwards compat | ||
// only ModuleNamespace will remain in next break! | ||
export { Loader, Module, Module as InternalModuleNamespace, Module as ModuleNamespace } | ||
@@ -15,3 +10,3 @@ /* | ||
*/ | ||
function arrayValues(arr) { | ||
function arrayValues (arr) { | ||
if (arr.values) | ||
@@ -24,7 +19,7 @@ return arr.values(); | ||
var iterable = {}; | ||
iterable[Symbol.iterator] = function() { | ||
iterable[Symbol.iterator] = function () { | ||
var keys = Object.keys(arr); | ||
var keyIndex = 0; | ||
return { | ||
next: function() { | ||
next: function () { | ||
if (keyIndex < keys.length) | ||
@@ -52,3 +47,3 @@ return { | ||
// 3.1.1 | ||
function Loader(baseKey) { | ||
function Loader (baseKey) { | ||
this.key = baseKey || baseURI; | ||
@@ -60,6 +55,11 @@ this.registry = new Registry(); | ||
// 3.3.2 | ||
Loader.prototype.import = function(key, parent) { | ||
Loader.prototype.import = function (key, parent) { | ||
if (typeof key !== 'string') | ||
throw new TypeError('Loader import method must be passed a module key string'); | ||
return this.load(key, parent); | ||
// custom resolveInstantiate combined hook for better perf | ||
return Promise.resolve(this[RESOLVE_INSTANTIATE](key, parent || this.key)) | ||
.then(Module.evaluate) | ||
.catch(function (err) { | ||
throw addToError(err, 'Loading ' + key + (parent ? ' from ' + parent : '')); | ||
}); | ||
}; | ||
@@ -69,9 +69,16 @@ // 3.3.3 | ||
// instantiate sets the namespace into the registry | ||
// it is up to implementations to ensure instantiate is debounced properly | ||
var INSTANTIATE = Loader.instantiate = createSymbol('instantiate'); | ||
/* | ||
* Combined resolve / instantiate hook | ||
* | ||
* Not in current reduced spec, but necessary to separate RESOLVE from RESOLVE + INSTANTIATE as described | ||
* in the spec notes of this repo to ensure that loader.resolve doesn't instantiate when not wanted. | ||
* | ||
* We implement RESOLVE_INSTANTIATE as a single hook instead of a separate INSTANTIATE in order to avoid | ||
* the need for double registry lookups as a performance optimization. | ||
*/ | ||
var RESOLVE_INSTANTIATE = Loader.resolveInstantiate = createSymbol('resolveInstantiate'); | ||
Loader.prototype.resolve = function(key, parent) { | ||
Loader.prototype.resolve = function (key, parent) { | ||
return this[RESOLVE](key, parent) | ||
.catch(function(err) { | ||
.catch(function (err) { | ||
throw addToError(err, 'Resolving ' + key + (parent ? ' to ' + parent : '')); | ||
@@ -81,32 +88,9 @@ }); | ||
// 3.3.4 | ||
Loader.prototype.load = function(key, parent) { | ||
var loader = this; | ||
var registry = loader.registry._registry; | ||
var resolvedKey; | ||
// there is the potential for an internal perf optimization to allow resolve to return { resolved, namespace } | ||
// but this needs to be done based on performance measurement | ||
return Promise.resolve(this[RESOLVE](key, parent || this.key)) | ||
.then(function(resolved) { | ||
var existingNamespace = registry[resolved]; | ||
if (existingNamespace) | ||
return Promise.resolve(existingNamespace); | ||
return loader[INSTANTIATE](resolved) | ||
.then(function(namespace) { | ||
// returning the namespace from instantiate can be considered a sort of perf optimization | ||
if (!namespace) | ||
namespace = loader.registry.get(resolvedKey); | ||
else if (!(namespace instanceof ModuleNamespace)) | ||
throw new TypeError('Instantiate did not resolve a Module Namespace'); | ||
return namespace; | ||
}); | ||
}) | ||
.catch(function(err) { | ||
throw addToError(err, 'Loading ' + key + (resolvedKey ? ' as ' + resolvedKey : '') + (parent ? ' from ' + parent : '')); | ||
// 3.3.4 (import without evaluate) | ||
// this is not documented because the use of deferred evaluation as in Module.evaluate is not | ||
// documented, as it is not considered a stable feature to be encouraged | ||
Loader.prototype.load = function (key, parent) { | ||
return Promise.resolve(this[RESOLVE_INSTANTIATE](key, parent || this.key)) | ||
.catch(function (err) { | ||
throw addToError(err, 'Loading ' + key + (parent ? ' from ' + parent : '')); | ||
}); | ||
@@ -129,3 +113,3 @@ }; | ||
// 4.4.1 | ||
Registry.prototype.constructor = function() { | ||
Registry.prototype.constructor = function () { | ||
throw new TypeError('Custom registries cannot be created.'); | ||
@@ -136,3 +120,3 @@ }; | ||
// 4.4.2 | ||
Registry.prototype[Symbol.iterator] = function() { | ||
Registry.prototype[Symbol.iterator] = function () { | ||
return this.entries()[Symbol.iterator](); | ||
@@ -142,5 +126,5 @@ }; | ||
// 4.4.3 | ||
Registry.prototype.entries = function() { | ||
Registry.prototype.entries = function () { | ||
var registry = this._registry; | ||
return arrayValues(Object.keys(registry).map(function(key) { | ||
return arrayValues(Object.keys(registry).map(function (key) { | ||
return [key, registry[key]]; | ||
@@ -152,9 +136,9 @@ })); | ||
// 4.4.4 | ||
Registry.prototype.keys = function() { | ||
Registry.prototype.keys = function () { | ||
return arrayValues(Object.keys(this._registry)); | ||
}; | ||
// 4.4.5 | ||
Registry.prototype.values = function() { | ||
Registry.prototype.values = function () { | ||
var registry = this._registry; | ||
return arrayValues(Object.keys(registry).map(function(key) { | ||
return arrayValues(Object.keys(registry).map(function (key) { | ||
return registry[key]; | ||
@@ -164,8 +148,8 @@ })); | ||
// 4.4.6 | ||
Registry.prototype.get = function(key) { | ||
Registry.prototype.get = function (key) { | ||
return this._registry[key]; | ||
}; | ||
// 4.4.7 | ||
Registry.prototype.set = function(key, namespace) { | ||
if (!(namespace instanceof ModuleNamespace)) | ||
Registry.prototype.set = function (key, namespace) { | ||
if (!(namespace instanceof Module)) | ||
throw new Error('Registry must be set with an instance of Module Namespace'); | ||
@@ -176,7 +160,7 @@ this._registry[key] = namespace; | ||
// 4.4.8 | ||
Registry.prototype.has = function(key) { | ||
Registry.prototype.has = function (key) { | ||
return !!this._registry[key]; | ||
}; | ||
// 4.4.9 | ||
Registry.prototype.delete = function(key) { | ||
Registry.prototype.delete = function (key) { | ||
if (this._registry[key]) { | ||
@@ -195,28 +179,44 @@ //delete this._registry[key]; | ||
*/ | ||
function ModuleNamespace(baseObject, evaluate) { | ||
var ns = this; | ||
Object.keys(baseObject).forEach(function(key) { | ||
Object.defineProperty(ns, key, { | ||
configurable: false, | ||
enumerable: true, | ||
get: function () { | ||
return baseObject[key]; | ||
}, | ||
set: function() { | ||
throw new TypeError('Module exports cannot be changed externally.'); | ||
} | ||
}); | ||
var EVALUATE = createSymbol('evaluate'); | ||
var EVALUATION_CONTEXT = createSymbol('evaluationContext'); | ||
var BASE_OBJECT = createSymbol('baseObject'); | ||
var EVALUATE_ERROR = createSymbol() | ||
// 8.3.1 Reflect.Module | ||
/* | ||
* Best-effort simplified non-spec implementation based on | ||
* a baseObject referenced via getters. | ||
* | ||
* Allows: | ||
* | ||
* loader.registry.set('x', new Module({ default: 'x' })); | ||
* | ||
* Optional evaluation function provides experimental Module.evaluate | ||
* support for non-executed modules in registry. | ||
*/ | ||
function Module (baseObject, evaluate) { | ||
Object.defineProperty(this, BASE_OBJECT, { | ||
value: baseObject | ||
}); | ||
if (evaluate) | ||
Object.defineProperty(ns, '$__evaluate', { | ||
// evaluate defers namespace population | ||
if (evaluate) { | ||
Object.defineProperty(this, EVALUATE, { | ||
value: evaluate, | ||
configurable: true, | ||
writable: true | ||
}); | ||
} | ||
} | ||
else { | ||
Object.keys(baseObject).forEach(extendNamespace, this); | ||
} | ||
}; | ||
// 8.4.2 | ||
Module.prototype = Object.create(null); | ||
if (typeof Symbol !== 'undefined' && Symbol.toStringTag) | ||
ModuleNamespace.prototype[Symbol.toStringTag] = 'Module'; | ||
Module.prototype[Symbol.toStringTag] = 'Module'; | ||
else | ||
Object.defineProperty(ModuleNamespace.prototype, 'toString', { | ||
value: function() { | ||
Object.defineProperty(Module.prototype, 'toString', { | ||
value: function () { | ||
return '[object Module]'; | ||
@@ -226,36 +226,40 @@ } | ||
// 8.3.1 Reflect.Module | ||
function Module(descriptors, executor, evaluate) { | ||
if (typeof descriptors !== 'object') | ||
throw new TypeError('Expected descriptors object'); | ||
// instead of providing a mutator, just provide the base object | ||
var baseObject = {}; | ||
// 8.2.1 ParseExportsDescriptors | ||
Object.keys(descriptors).forEach(function(key) { | ||
var descriptor = descriptors[key]; | ||
if (!('value' in descriptor)) | ||
throw new TypeError('Error reading descriptor for "' + key + '" - module polyfill only supports value descriptors currently'); | ||
baseObject[key] = descriptor.value; | ||
function extendNamespace (key) { | ||
Object.defineProperty(this, key, { | ||
enumerable: true, | ||
get: function () { | ||
return this[BASE_OBJECT][key]; | ||
}, | ||
set: function () { | ||
throw new TypeError('Module exports cannot be changed externally.'); | ||
} | ||
}); | ||
} | ||
var ns = new ModuleNamespace(baseObject, evaluate); | ||
function doEvaluate (evaluate, context) { | ||
try { | ||
evaluate.call(context); | ||
} | ||
catch (e) { | ||
return e; | ||
} | ||
} | ||
if (executor) | ||
executor(baseObject, ns); | ||
// 8.4.1 Module.evaluate... not documented or used because this is potentially unstable | ||
Module.evaluate = function (ns) { | ||
var evaluate = ns[EVALUATE]; | ||
if (evaluate) { | ||
ns[EVALUATE] = undefined; | ||
var err = doEvaluate(evaluate); | ||
if (err) { | ||
// cache the error | ||
ns[EVALUATE] = function () { | ||
throw err; | ||
}; | ||
throw err; | ||
} | ||
Object.keys(ns[BASE_OBJECT]).forEach(extendNamespace, ns); | ||
} | ||
// make chainable | ||
return ns; | ||
}; | ||
// 8.4.2 | ||
Module.prototype = null; | ||
// 8.4.1 Module.evaluate | ||
Module.evaluate = function(ns) { | ||
if (ns.$__evaluate) { | ||
ns.$__evaluate(); | ||
ns.$__evaluate = undefined; | ||
} | ||
}; |
@@ -1,8 +0,8 @@ | ||
import { Loader, Module, InternalModuleNamespace as ModuleNamespace } from './loader-polyfill.js'; | ||
import { Loader, Module, } from './loader-polyfill.js'; | ||
import { resolveUrlToParentIfNotPlain } from './resolve.js'; | ||
import { addToError, global } from './common.js'; | ||
import { addToError, global, createSymbol } from './common.js'; | ||
export default RegisterLoader; | ||
export var emptyModule = new ModuleNamespace({}); | ||
export var emptyModule = new Module({}); | ||
@@ -19,10 +19,14 @@ /* | ||
*/ | ||
function RegisterLoader(baseKey) { | ||
var REGISTER_REGISTRY = createSymbol('registerRegistry'); | ||
var REGISTERED_LAST_ANON = createSymbol('registeredLastAnon'); | ||
function RegisterLoader (baseKey) { | ||
Loader.apply(this, arguments); | ||
// last anonymous System.register call | ||
this._registeredLastAnon = undefined; | ||
this[REGISTERED_LAST_ANON] = undefined; | ||
// in-flight es module load records | ||
this._registerRegistry = {}; | ||
this[REGISTER_REGISTRY] = {}; | ||
@@ -38,306 +42,358 @@ // tracing | ||
// these are implementation specific | ||
// this allows a v2 migration path into symbols so normalize and instantiate | ||
// aren't exposed to end-users | ||
// NB replace with createSymbol('normalize'), ... for next major | ||
RegisterLoader.normalize = 'normalize'; | ||
RegisterLoader.instantiate = 'instantiate'; | ||
RegisterLoader.createMetadata = 'createMetadata'; | ||
RegisterLoader.processRegisterContext = 'processRegisterContext'; | ||
// default normalize is the WhatWG style normalizer | ||
RegisterLoader.prototype.normalize = function(key, parentKey, metadata) { | ||
RegisterLoader.prototype.normalize = function (key, parentKey, metadata) { | ||
return resolveUrlToParentIfNotPlain(key, parentKey); | ||
}; | ||
RegisterLoader.prototype.instantiate = function(key, metadata) {}; | ||
RegisterLoader.prototype.instantiate = function (key, metadata) {}; | ||
// this function is an optimization to allow loader extensions to | ||
// this function is an optimization to allow loader extensions to | ||
// implement it to set the metadata object shape upfront to ensure | ||
// it can run as a single hidden class throughout the normalize | ||
// and instantiate pipeline hooks in the js engine | ||
RegisterLoader.prototype.createMetadata = function() { | ||
RegisterLoader.prototype.createMetadata = function () { | ||
return {}; | ||
}; | ||
function ensureResolution (resolvedKey) { | ||
if (resolvedKey === undefined) | ||
throw new RangeError('No resolution found.'); | ||
return resolvedKey; | ||
} | ||
function resolve (loader, key, parentKey, metadata) { | ||
return Promise.resolve() | ||
.then(function () { | ||
return loader.normalize(key, parentKey, metadata); | ||
}) | ||
.then(ensureResolution); | ||
} | ||
var RESOLVE = Loader.resolve; | ||
RegisterLoader.prototype[RESOLVE] = function(key, parentKey) { | ||
var loader = this; | ||
var registry = loader.registry._registry; | ||
// normalization shortpath if already in the registry or loading | ||
if (loader._registerRegistry[key] || registry[key]) | ||
RegisterLoader.prototype[RESOLVE] = function (key, parentKey) { | ||
if (loader[REGISTER_REGISTRY][key] || loader.registry._registry[key]) | ||
return Promise.resolve(key); | ||
var metadata = this.createMetadata(); | ||
return Promise.resolve(loader.normalize(key, parentKey, metadata)) | ||
.then(function(resolvedKey) { | ||
if (resolvedKey === undefined) | ||
throw new RangeError('No resolution normalizing "' + key + '" to ' + parentKey); | ||
// we create the in-progress load record already here to store the normalization metadata | ||
if (!registry[resolvedKey]) | ||
(loader._registerRegistry[resolvedKey] || createLoadRecord(loader, resolvedKey)).metadata = metadata; | ||
return resolvedKey; | ||
}); | ||
return resolve(this, key, parentKey, this.createMetadata()); | ||
}; | ||
// provides instantiate promise cache | ||
// we need to first wait on instantiate which will tell us if it is ES or not | ||
// this record represents that waiting period, and when set, we then populate | ||
// the esLinkRecord record into this load record. | ||
// instantiate is a promise for a module namespace or undefined | ||
function createLoadRecord(loader, key) { | ||
return loader._registerRegistry[key] = { | ||
// once evaluated, the linkRecord is set to undefined leaving just the other load record properties | ||
// this allows tracking new binding listeners for es modules through importerSetters | ||
// for dynamic modules, the load record is removed entirely. | ||
function createLoadRecord (key, registration) { | ||
return this[REGISTER_REGISTRY][key] = { | ||
key: key, | ||
metadata: undefined, | ||
// define cache | ||
defined: undefined, | ||
// in-flight | ||
instantiatePromise: undefined, | ||
// loaded | ||
// defined System.register cache | ||
registration: registration, | ||
// module namespace object | ||
module: undefined, | ||
// es-specific | ||
esLinkRecord: undefined, | ||
importerSetters: undefined | ||
// es-only | ||
// this sticks around so new module loads can listen to binding changes | ||
// for already-loaded modules by adding themselves to their importerSetters | ||
importerSetters: undefined, | ||
// in-flight linking record | ||
linkRecord: { | ||
metadata: undefined, | ||
// promise for instantiated | ||
instantiatePromise: undefined, | ||
dependencies: undefined, | ||
execute: undefined, | ||
// underlying module object bindings | ||
moduleObj: undefined, | ||
// es only, also indicates if es or not | ||
setters: undefined, | ||
// promise for instantiated dependencies (dependencyInstantiations populated) | ||
depsInstantiatePromise: undefined, | ||
// will be the array of dependency load record or a module namespace | ||
dependencyInstantiations: [], | ||
// indicates if the load and all its dependencies are instantiated and linked | ||
// but not yet executed | ||
// mostly just a performance shortpath to avoid rechecking the promises above | ||
linked: false, | ||
error: undefined | ||
// NB optimization and way of ensuring module objects in setters | ||
// indicates setters which should run pre-execution of that dependency | ||
// hoisted: undefined | ||
} | ||
}; | ||
} | ||
RegisterLoader.prototype[Loader.instantiate] = function(key) { | ||
RegisterLoader.prototype[Loader.resolveInstantiate] = function (key, parentKey) { | ||
var loader = this; | ||
return instantiate(this, key) | ||
.then(function(instantiated) { | ||
if (instantiated instanceof ModuleNamespace) | ||
return Promise.resolve(instantiated); | ||
var registry = loader.registry._registry; | ||
var registerRegistry = loader[REGISTER_REGISTRY]; | ||
return instantiateAllDeps(loader, instantiated, []) | ||
.then(function() { | ||
var err = ensureEvaluated(loader, instantiated, []); | ||
if (err) | ||
return Promise.reject(err); | ||
return resolveInstantiate(loader, key, parentKey, registry, registerRegistry) | ||
.then(function (instantiated) { | ||
if (instantiated instanceof Module) | ||
return instantiated; | ||
if (loader.trace) | ||
traceLoadRecord(loader, instantiated, []); | ||
return instantiated.module || emptyModule; | ||
// resolveInstantiate always returns a load record with a link record and no module value | ||
if (instantiated.linkRecord.linked) | ||
return ensureEvaluate(loader, instantiated, instantiated.linkRecord, registry, registerRegistry); | ||
return instantiateDeps(loader, instantiated, instantiated.linkRecord, registry, registerRegistry, [instantiated]) | ||
.then(function () { | ||
return ensureEvaluate(loader, instantiated, instantiated.linkRecord, registry, registerRegistry); | ||
}) | ||
.catch(function(err) { | ||
.catch(function (err) { | ||
clearLoadErrors(loader, instantiated); | ||
throw err; | ||
}); | ||
}) | ||
}); | ||
}; | ||
// instantiates the given module name | ||
// returns the load record for es or the namespace object for dynamic | ||
// setting the dynamic namespace into the registry | ||
function instantiate(loader, key) { | ||
var load = loader._registerRegistry[key]; | ||
function resolveInstantiate (loader, key, parentKey, registry, registerRegistry) { | ||
// normalization shortpaths for already-normalized key | ||
// could add a plain name filter, but doesn't yet seem necessary for perf | ||
var module = registry[key]; | ||
if (module) | ||
return Promise.resolve(module); | ||
// this is impossible assuming resolve always runs before instantiate | ||
if (!load) | ||
throw new TypeError('Internal error, load record not created'); | ||
var load = registerRegistry[key]; | ||
return load.instantiatePromise || (load.instantiatePromise = Promise.resolve(loader.instantiate(key, load.metadata)) | ||
.then(function(instantiation) { | ||
// dynamic module | ||
if (instantiation !== undefined) { | ||
loader.registry._registry[key] = instantiation; | ||
loader._registerRegistry[key] = undefined; | ||
return instantiation; | ||
} | ||
// already linked but not in main registry is ignored | ||
if (load && !load.module) | ||
return instantiate(loader, load, load.linkRecord, registry, registerRegistry); | ||
// run the cached loader.register declaration if there is one | ||
ensureRegisterLinkRecord.call(loader, load); | ||
var metadata = loader.createMetadata(); | ||
return resolve(loader, key, parentKey, metadata) | ||
.catch(function (err) { | ||
throw addToError(err, 'Resolving dependency "' + key + '" of ' + parentKey); | ||
}) | ||
.then(function (resolvedKey) { | ||
// main loader registry always takes preference | ||
module = registry[resolvedKey]; | ||
if (module) | ||
return module; | ||
// metadata no longer needed | ||
if (!loader.trace) | ||
load.metadata = undefined; | ||
load = registerRegistry[resolvedKey]; | ||
return load; | ||
}) | ||
.catch(function(err) { | ||
err = addToError(err, 'Instantiating ' + load.key); | ||
// already has a module value but not already in the registry (load.module) | ||
// means it was removed by registry.delete, so we should | ||
// disgard the current load record creating a new one over it | ||
// but keep any existing registration | ||
if (!load || load.module) | ||
load = createLoadRecord.call(loader, resolvedKey, load && load.registration); | ||
// immediately clear the load record for an instantiation error | ||
if (loader._registerRegistry[load.key] === load) | ||
loader._registerRegistry[load.key] = undefined; | ||
var link = load.linkRecord; | ||
if (!link) | ||
return load; | ||
throw err; | ||
})); | ||
link.metadata = link.metadata || metadata; | ||
return instantiate(loader, load, link, registry, registerRegistry); | ||
}); | ||
} | ||
// this only applies to load records with load.esLinkRecord set | ||
function instantiateAllDeps(loader, load, seen) { | ||
// skip if already linked | ||
if (load.module) | ||
return Promise.resolve(); | ||
function instantiate (loader, load, link, registry, registerRegistry) { | ||
return link.instantiatePromise || (link.instantiatePromise = | ||
// if there is already an existing registration, skip running instantiate | ||
(load.registration ? Promise.resolve() : Promise.resolve().then(function () { | ||
return loader.instantiate(load.key, link.metadata); | ||
})) | ||
.then(function (instantiation) { | ||
// direct module return from instantiate -> we're done | ||
if (instantiation !== undefined) { | ||
if (!(instantiation instanceof Module)) | ||
throw new TypeError('Instantiate did not return a valid Module object.'); | ||
var esLinkRecord = load.esLinkRecord; | ||
registerRegistry[load.key] = undefined; | ||
if (loader.trace) | ||
traceLoad(load, link); | ||
return registry[load.key] = instantiation; | ||
} | ||
// no dependencies shortpath | ||
if (!esLinkRecord.dependencies.length) | ||
return Promise.resolve(); | ||
// run the cached loader.register declaration if there is one | ||
var registration = load.registration; | ||
// clear to allow new registrations for future loads (combined with registry delete) | ||
load.registration = undefined; | ||
if (!registration) | ||
throw new TypeError('Module instantiation did not call an anonymous or correctly named System.register.'); | ||
// assumes seen does not contain load already | ||
seen.push(load); | ||
link.dependencies = registration[0]; | ||
var instantiateDepsPromises = Array(esLinkRecord.dependencies.length); | ||
var registry = loader.registry._registry; | ||
load.importerSetters = []; | ||
// normalize dependencies | ||
for (var i = 0; i < esLinkRecord.dependencies.length; i++) (function(i) { | ||
// this resolve can potentially be cached on the link record, should be a measured optimization | ||
instantiateDepsPromises[i] = loader[RESOLVE](esLinkRecord.dependencies[i], load.key) | ||
.catch(function(err) { | ||
throw addToError(err, 'Resolving ' + esLinkRecord.dependencies[i] + ' to ' + load.key); | ||
}) | ||
.then(function(resolvedDepKey) { | ||
if (loader.trace) { | ||
esLinkRecord.depMap = esLinkRecord.depMap || {}; | ||
esLinkRecord.depMap[esLinkRecord.dependencies[i]] = resolvedDepKey; | ||
} | ||
// process System.registerDynamic declaration | ||
if (registration[2]) | ||
registerDynamic(loader, load, link, registry, registerRegistry, registration[1]); | ||
var existingNamespace = registry[resolvedDepKey]; | ||
if (existingNamespace) { | ||
esLinkRecord.dependencyInstantiations[i] = existingNamespace; | ||
// run setter to reference the module | ||
if (esLinkRecord.setters[i]) | ||
esLinkRecord.setters[i](existingNamespace); | ||
return Promise.resolve(); | ||
} | ||
// process System.register declaration | ||
else | ||
registerDeclarative(loader, load, link, registration[1]); | ||
return instantiate(loader, resolvedDepKey) | ||
.then(function(instantiation) { | ||
// instantiation is either a load record or a module namespace | ||
esLinkRecord.dependencyInstantiations[i] = instantiation; | ||
// shortpath to instantiateDeps | ||
if (!link.dependencies.length) { | ||
link.linked = true; | ||
if (loader.trace) | ||
traceLoad(load, link); | ||
} | ||
// dynamic module | ||
if (instantiation instanceof ModuleNamespace) { | ||
if (esLinkRecord.setters[i]) | ||
esLinkRecord.setters[i](instantiation); | ||
return Promise.resolve(); | ||
} | ||
return load; | ||
}) | ||
.catch(function (err) { | ||
throw link.error = addToError(err, 'Instantiating ' + load.key); | ||
})); | ||
} | ||
// register setter with dependency | ||
instantiation.importerSetters.push(esLinkRecord.setters[i]); | ||
// like resolveInstantiate, but returning load records for linking | ||
function resolveInstantiateDep (loader, key, parentKey, registry, registerRegistry, traceDepMap) { | ||
// normalization shortpaths for already-normalized key | ||
// could add a plain name filter, but doesn't yet seem necessary for perf | ||
var load = registerRegistry[key]; | ||
var module = registry[key]; | ||
// run setter now to pick up the first bindings from the dependency | ||
if (esLinkRecord.setters[i]) | ||
esLinkRecord.setters[i](instantiation.esLinkRecord.moduleObj); | ||
if (module) { | ||
if (traceDepMap) | ||
traceDepMap[key] = key; | ||
// circular | ||
if (seen.indexOf(instantiation) !== -1) | ||
return Promise.resolve(); | ||
// registry authority check in case module was deleted or replaced in main registry | ||
if (load && load.module && load.module === module) | ||
return load; | ||
else | ||
return module; | ||
} | ||
// es module load | ||
// already linked but not in main registry is ignored | ||
if (load && !load.module) { | ||
if (traceDepMap) | ||
traceDepMap[key] = key; | ||
return instantiate(loader, load, load.linkRecord, registry, registerRegistry); | ||
} | ||
// if not already linked, instantiate dependencies | ||
if (instantiation.esLinkRecord) | ||
return instantiateAllDeps(loader, instantiation, seen); | ||
}); | ||
}) | ||
})(i); | ||
var metadata = loader.createMetadata(); | ||
return resolve(loader, key, parentKey, metadata) | ||
.catch(function (err) { | ||
throw addToError(err, 'Resolving dependency "' + key + '" of ' + parentKey); | ||
}) | ||
.then(function (resolvedKey) { | ||
if (traceDepMap) | ||
traceDepMap[key] = key; | ||
return Promise.all(instantiateDepsPromises) | ||
.catch(function(err) { | ||
err = addToError(err, 'Loading ' + load.key); | ||
// similar logic to above | ||
load = registerRegistry[resolvedKey]; | ||
module = registry[resolvedKey]; | ||
// throw up the instantiateAllDeps stack | ||
// loads are then synchonously cleared at the top-level through the helper below | ||
// this then ensures avoiding partially unloaded tree states | ||
esLinkRecord.error = err; | ||
// main loader registry always takes preference | ||
if (module && (!load || load.module && module !== load.module)) | ||
return module; | ||
throw err; | ||
// already has a module value but not already in the registry (load.module) | ||
// means it was removed by registry.delete, so we should | ||
// disgard the current load record creating a new one over it | ||
// but keep any existing registration | ||
if (!load || !module && load.module) | ||
load = createLoadRecord.call(loader, resolvedKey, load && load.registration); | ||
var link = load.linkRecord; | ||
if (!link) | ||
return load; | ||
link.metadata = link.metadata || metadata; | ||
return instantiate(loader, load, link, registry, registerRegistry); | ||
}); | ||
} | ||
// clears an errored load and all its errored dependencies from the loads registry | ||
function clearLoadErrors(loader, load) { | ||
// clear from loads | ||
if (loader._registerRegistry[load.key] === load) | ||
loader._registerRegistry[load.key] = undefined; | ||
function traceLoad (load, link) { | ||
loader.loads[load.key] = { | ||
key: load.key, | ||
dependencies: link.dependencies, | ||
depMap: link.depMap || {}, | ||
metadata: link.metadata | ||
}; | ||
} | ||
var esLinkRecord = load.esLinkRecord; | ||
function registerDynamic (loader, load, link, registry, registerRegistry, execute) { | ||
var moduleObj = link.moduleObj = {}; | ||
if (!esLinkRecord) | ||
return; | ||
// create a closure on dependencies and dependencyInstantiations only | ||
var dependencyInstantiations = link.dependencyInstantiations = []; | ||
var dependencies = link.dependencies; | ||
var key = load.key; | ||
esLinkRecord.dependencyInstantiations.forEach(function(depLoad, index) { | ||
if (!depLoad || depLoad instanceof ModuleNamespace) | ||
return; | ||
// we can only require from already-known dependencies | ||
function require (name) { | ||
for (var i = 0; i < dependencies.length; i++) { | ||
if (dependencies[i] === name) { | ||
var depLoad = dependencyInstantiations[i]; | ||
var err; | ||
if (depLoad.esLinkRecord && depLoad.esLinkRecord.error) { | ||
// unregister setters for es dependency load records | ||
var setterIndex = depLoad.importerSetters.indexOf(esLinkRecord.setters[index]); | ||
depLoad.importerSetters.splice(setterIndex, 1); | ||
var module; | ||
// provides a circular reference check | ||
if (loader._registerRegistry[depLoad.key] === depLoad) | ||
clearLoadErrors(loader, depLoad); | ||
if (depLoad instanceof Module) | ||
module = depLoad; | ||
else | ||
module = ensureEvaluate(loader, depLoad, depLoad.linkRecord, registry, registerRegistry); | ||
return module.__useDefault ? module.default : module; | ||
} | ||
} | ||
}); | ||
} | ||
throw new Error('Module ' + name + ' not declared as a System.registerDynamic dependency of ' + key); | ||
} | ||
function createESLinkRecord(dependencies, setters, module, moduleObj, execute) { | ||
return { | ||
dependencies: dependencies, | ||
error: undefined, | ||
// will be the dependency ES load record, or a module namespace | ||
dependencyInstantiations: Array(dependencies.length), | ||
setters: setters, | ||
module: module, | ||
moduleObj: moduleObj, | ||
execute: execute | ||
}; | ||
link.execute = function () { | ||
var exports = moduleObj.default = {}; | ||
Object.defineProperty(moduleObj, '__useDefault', { value: true }); | ||
var module = { exports: exports, id: key }; | ||
// execute then copy the exports onto moduleObj | ||
copyNamedExports(execute(require, exports, module) || module.exports, moduleObj); | ||
} | ||
} | ||
/* | ||
* System.register | ||
* Places the status into the registry and a load into the loads list | ||
* Convert a CJS module.exports into a valid object for new Module: | ||
* | ||
* new Module(getEsModule(module.exports)) | ||
* | ||
* Sets the default value to the module, while also reading off named exports carefully. | ||
*/ | ||
RegisterLoader.prototype.register = function(key, deps, declare) { | ||
// anonymous modules get stored as lastAnon | ||
if (declare === undefined) | ||
this._registeredLastAnon = [key, deps]; | ||
// everything else registers into the register cache | ||
else | ||
(this._registerRegistry[key] || createLoadRecord(this, key)).defined = [deps, declare]; | ||
}; | ||
RegisterLoader.prototype.processRegisterContext = function(contextKey) { | ||
if (!this._registeredLastAnon) | ||
function copyNamedExports(exports, moduleObj) { | ||
// don't trigger getters/setters in environments that support them | ||
if ((typeof exports != 'object' && typeof exports != 'function') || exports === global) | ||
return; | ||
(this._registerRegistry[contextKey] || createLoadRecord(this, contextKey)).defined = this._registeredLastAnon; | ||
this._registeredLastAnon = undefined; | ||
}; | ||
for (var p in exports) | ||
if (p !== 'default') | ||
defineOrCopyProperty(moduleObj, exports, p); | ||
function ensureRegisterLinkRecord(load) { | ||
// ensure we already have a link record | ||
if (load.esLinkRecord) | ||
return; | ||
moduleObj.default = exports; | ||
} | ||
var key = load.key; | ||
var registrationPair = load.defined; | ||
function defineOrCopyProperty(targetObj, sourceObj, propName) { | ||
try { | ||
var d; | ||
if (d = Object.getOwnPropertyDescriptor(sourceObj, propName)) | ||
Object.defineProperty(targetObj, propName, d); | ||
} | ||
catch (ex) { | ||
// Object.getOwnPropertyDescriptor threw an exception, fall back to normal set property | ||
// we dont need hasOwnProperty here because getOwnPropertyDescriptor would have returned undefined above | ||
targetObj[propName] = sourceObj[propName]; | ||
} | ||
} | ||
if (!registrationPair) | ||
throw new TypeError('Module instantiation did not call an anonymous or correctly named System.register'); | ||
function registerDeclarative (loader, load, link, declare) { | ||
var moduleObj = link.moduleObj = {}; | ||
var importerSetters = load.importerSetters; | ||
load.defined = undefined; | ||
var importerSetters = []; | ||
var moduleObj = {}; | ||
var locked = false; | ||
var declared = registrationPair[1].call(global, function(name, value) { | ||
// closure especially not based on link to allow link record disposal | ||
var declared = declare.call(global, function (name, value) { | ||
// export setter propogation with locking to avoid cycles | ||
@@ -355,139 +411,306 @@ if (locked) | ||
if (importerSetters.length) { | ||
locked = true; | ||
for (var i = 0; i < importerSetters.length; i++) | ||
// this object should be a defined module object | ||
// but in order to do that we need the exports returned by declare | ||
// for now we assume no exports in the implementation | ||
importerSetters[i](moduleObj); | ||
locked = true; | ||
for (var i = 0; i < importerSetters.length; i++) | ||
importerSetters[i](moduleObj); | ||
locked = false; | ||
locked = false; | ||
} | ||
return value; | ||
}, new ContextualLoader(this, key)); | ||
}, new ContextualLoader(loader, load.key)); | ||
var setters, execute; | ||
if (typeof declared !== 'function') { | ||
setters = declared.setters; | ||
execute = declared.execute; | ||
link.setters = declared.setters; | ||
link.execute = declared.execute; | ||
} | ||
else { | ||
setters = [], | ||
execute = declared; | ||
link.setters = []; | ||
link.execute = declared; | ||
} | ||
} | ||
// TODO, pass module when we can create it here already via exports | ||
load.importerSetters = importerSetters; | ||
load.esLinkRecord = createESLinkRecord(registrationPair[0], setters, undefined, moduleObj, execute); | ||
function instantiateDeps (loader, load, link, registry, registerRegistry, seen) { | ||
return (link.depsInstantiatePromise || (link.depsInstantiatePromise = Promise.resolve() | ||
.then(function () { | ||
var depsInstantiatePromises = Array(link.dependencies.length); | ||
for (var i = 0; i < link.dependencies.length; i++) | ||
depsInstantiatePromises[i] = resolveInstantiateDep(loader, link.dependencies[i], load.key, registry, registerRegistry, loader.trace && (link.depMap = {})); | ||
return Promise.all(depsInstantiatePromises); | ||
}) | ||
.then(function (dependencyInstantiations) { | ||
// for registerDynamic, we need dependencyInstantiations | ||
// to work by reference as we have a closure on it | ||
if (!link.setters) { | ||
for (var i = 0; i < dependencyInstantiations.length; i++) | ||
link.dependencyInstantiations[i] = dependencyInstantiations[i]; | ||
} | ||
// run setters to set up bindings to instantiated dependencies | ||
else { | ||
link.dependencyInstantiations = dependencyInstantiations; | ||
for (var i = 0; i < dependencyInstantiations.length; i++) { | ||
var setter = link.setters[i]; | ||
if (setter) { | ||
var instantiation = dependencyInstantiations[i]; | ||
if (instantiation instanceof Module) { | ||
setter(instantiation); | ||
} | ||
else { | ||
setter(instantiation.module || instantiation.linkRecord.moduleObj); | ||
// this applies to both es and dynamic registrations | ||
if (instantiation.importerSetters) | ||
instantiation.importerSetters.push(setter); | ||
} | ||
} | ||
} | ||
} | ||
}))) | ||
.then(function () { | ||
// now deeply instantiateDeps on each dependencyInstantiation that is a load record | ||
var deepDepsInstantiatePromises = []; | ||
for (var i = 0; i < link.dependencies.length; i++) { | ||
var depLoad = link.dependencyInstantiations[i]; | ||
var depLink = depLoad.linkRecord; | ||
if (!depLink || depLink.linked) | ||
continue; | ||
if (seen.indexOf(depLoad) !== -1) | ||
continue; | ||
seen.push(depLoad); | ||
deepDepsInstantiatePromises.push(instantiateDeps(loader, depLoad, depLoad.linkRecord, registry, registerRegistry, seen)); | ||
} | ||
return Promise.all(deepDepsInstantiatePromises); | ||
}) | ||
.then(function () { | ||
// as soon as all dependencies instantiated, we are ready for evaluation so can add to the registry | ||
// this can run multiple times, but so what | ||
link.linked = true; | ||
if (loader.trace) | ||
traceLoad(load, link); | ||
return load; | ||
}) | ||
.catch(function (err) { | ||
err = addToError(err, 'Loading ' + load.key); | ||
// throw up the instantiateDeps stack | ||
// loads are then synchonously cleared at the top-level through the clearLoadErrors helper below | ||
// this then ensures avoiding partially unloaded tree states | ||
link.error = link.error || err; | ||
throw err; | ||
}); | ||
} | ||
// clears an errored load and all its errored dependencies from the loads registry | ||
function clearLoadErrors (loader, load) { | ||
// clear from loads | ||
if (loader[REGISTER_REGISTRY][load.key] === load) | ||
loader[REGISTER_REGISTRY][load.key] = undefined; | ||
var link = load.linkRecord; | ||
if (!link) | ||
return; | ||
if (link.dependencyInstantiations) | ||
link.dependencyInstantiations.forEach(function (depLoad, index) { | ||
if (!depLoad || depLoad instanceof Module) | ||
return; | ||
if (depLoad.linkRecord) { | ||
if (depLoad.linkRecord.error) { | ||
// provides a circular reference check | ||
if (loader[REGISTER_REGISTRY][depLoad.key] === depLoad) | ||
clearLoadErrors(loader, depLoad); | ||
} | ||
// unregister setters for es dependency load records that will remain | ||
if (link.setters && depLoad.importerSetters) { | ||
var setterIndex = depLoad.importerSetters.indexOf(link.setters[index]); | ||
depLoad.importerSetters.splice(setterIndex, 1); | ||
} | ||
} | ||
}); | ||
} | ||
/* | ||
* System.register | ||
*/ | ||
RegisterLoader.prototype.register = function (key, deps, declare) { | ||
// anonymous modules get stored as lastAnon | ||
if (declare === undefined) { | ||
this[REGISTERED_LAST_ANON] = [key, deps, false]; | ||
} | ||
// everything else registers into the register cache | ||
else { | ||
var load = this[REGISTER_REGISTRY][key] || createLoadRecord.call(this, key, undefined); | ||
load.registration = [deps, declare, false]; | ||
} | ||
}; | ||
/* | ||
* System.registerDyanmic | ||
*/ | ||
RegisterLoader.prototype.registerDynamic = function (key, deps, execute) { | ||
// anonymous modules get stored as lastAnon | ||
if (typeof key !== 'string') { | ||
this[REGISTERED_LAST_ANON] = [key, deps === true && execute || deps === false && makeNonExecutingRequire(key, execute) || deps, true]; | ||
} | ||
// everything else registers into the register cache | ||
else { | ||
var load = this[REGISTER_REGISTRY][key] || createLoadRecord.call(this, key, undefined); | ||
load.registration = [deps, execute === true && arguments[3] || execute === false && makeNonExecutingRequire(deps, arguments[3]) || execute, true]; | ||
} | ||
}; | ||
function makeNonExecutingRequire (deps, execute) { | ||
return function(require) { | ||
// evaluate deps first | ||
for (var i = 0; i < deps.length; i++) | ||
require(deps[i]); | ||
// then run execution function | ||
return execute.apply(this, arguments); | ||
}; | ||
} | ||
RegisterLoader.prototype.processRegisterContext = function (contextKey) { | ||
var registeredLastAnon = this[REGISTERED_LAST_ANON]; | ||
if (!registeredLastAnon) | ||
return; | ||
this[REGISTERED_LAST_ANON] = undefined; | ||
// returning the defined value allows avoiding an extra lookup for custom instantiate | ||
var load = this[REGISTER_REGISTRY][contextKey] || createLoadRecord.call(this, contextKey, undefined); | ||
load.registration = registeredLastAnon; | ||
}; | ||
// ContextualLoader class | ||
// backwards-compatible with previous System.register context argument by exposing .id | ||
function ContextualLoader(loader, key) { | ||
function ContextualLoader (loader, key) { | ||
this.loader = loader; | ||
this.key = this.id = key; | ||
} | ||
ContextualLoader.prototype.constructor = function() { | ||
ContextualLoader.prototype.constructor = function () { | ||
throw new TypeError('Cannot subclass the contextual loader only Reflect.Loader.'); | ||
}; | ||
ContextualLoader.prototype.import = function(key) { | ||
ContextualLoader.prototype.import = function (key) { | ||
return this.loader.import(key, this.key); | ||
}; | ||
ContextualLoader.prototype.resolve = function(key) { | ||
ContextualLoader.prototype.resolve = function (key) { | ||
return this.loader[Loader.resolve](key, this.key); | ||
}; | ||
ContextualLoader.prototype.load = function(key) { | ||
ContextualLoader.prototype.load = function (key) { | ||
return this.loader.load(key, this.key); | ||
}; | ||
// this is the execution function bound to the Module namespace record | ||
function ensureEvaluate (loader, load, link, registry, registerRegistry) { | ||
if (load.module) | ||
return load.module; | ||
if (link.error) | ||
throw link.error; | ||
var err = doEvaluate(load, link, registry, registerRegistry, []); | ||
if (err) { | ||
clearLoadErrors(loader, load); | ||
throw err; | ||
} | ||
return load.module; | ||
} | ||
// ensures the given es load is evaluated | ||
// returns the error if any | ||
function ensureEvaluated(loader, load, seen) { | ||
var esLinkRecord = load.esLinkRecord; | ||
// no esLinkRecord means evaluated | ||
if (!esLinkRecord) | ||
return; | ||
// assumes seen does not contain load already | ||
function doEvaluate (load, link, registry, registerRegistry, seen) { | ||
seen.push(load); | ||
var err, depLoad; | ||
var err; | ||
for (var i = 0; i < esLinkRecord.dependencies.length; i++) { | ||
depLoad = esLinkRecord.dependencyInstantiations[i]; | ||
// es modules evaluate dependencies first | ||
// non es modules explicitly call moduleEvaluate through require | ||
if (link.setters) { | ||
var depLoad, depLink; | ||
for (var i = 0; i < link.dependencies.length; i++) { | ||
depLoad = link.dependencyInstantiations[i]; | ||
// non ES load | ||
// custom Module returned from instantiate | ||
// it is the responsibility of the executor to remove the module from the registry on failure | ||
if (depLoad instanceof Module) { | ||
err = nsEvaluate(depLoad); | ||
} | ||
// it is the responsibility of the executor to remove the module from the registry on failure | ||
if (depLoad instanceof ModuleNamespace) | ||
err = namespaceEvaluate(depLoad); | ||
// ES or dynamic execute | ||
else { | ||
depLink = depLoad.linkRecord; | ||
if (depLink && !depLink.module && seen.indexOf(depLoad) === -1) { | ||
if (depLink.error) | ||
err = depLink.error; | ||
else | ||
err = doEvaluate(depLoad, depLink, registry, registerRegistry, seen); | ||
} | ||
} | ||
// ES load | ||
else if (seen.indexOf(depLoad) === -1) | ||
err = ensureEvaluated(loader, depLoad, seen); | ||
if (err) | ||
return addToError(err, 'Evaluating ' + load.key); | ||
if (err) | ||
return link.error = addToError(err, 'Evaluating ' + load.key); | ||
} | ||
} | ||
// es load record evaluation | ||
err = esEvaluate(esLinkRecord); | ||
// link.execute won't exist for Module returns from instantiate on top-level load | ||
if (link.execute) | ||
// "this" is null in ES, exports in CJS | ||
err = doExecute(link.execute, link.setters ? nullContext : link.moduleObj.default); | ||
if (err) | ||
return addToError(err, 'Evaluating ' + load.key); | ||
return link.error = addToError(err, 'Evaluating ' + load.key); | ||
load.module = new ModuleNamespace(esLinkRecord.moduleObj); | ||
loader.registry._registry[load.key] = load.module; | ||
// can clear link record now | ||
if (!loader.trace) | ||
load.esLinkRecord = undefined; | ||
registry[load.key] = load.module = new Module(load.linkRecord.moduleObj); | ||
// if not an esm module, run importer setters and clear them | ||
// this allows dynamic modules to update themselves into es modules | ||
// as soon as execution has completed | ||
if (!link.setters) { | ||
if (load.importerSetters) | ||
for (var i = 0; i < load.importerSetters.length; i++) | ||
load.importerSetters[i](load.module); | ||
// once executed, non-es modules can be removed from the private registry | ||
// since we don't need to store binding update metadata | ||
if (registerRegistry[load.key] === load) | ||
registerRegistry[load.key] = undefined; | ||
} | ||
load.linkRecord = undefined; | ||
} | ||
var execContext = {}; | ||
// {} is the closest we can get to call(undefined) | ||
var nullContext = {}; | ||
if (Object.freeze) | ||
Object.freeze(execContext); | ||
function esEvaluate(esLinkRecord) { | ||
Object.freeze(nullContext); | ||
function doExecute (execute, context) { | ||
try { | ||
// {} is the closest we can get to call(undefined) | ||
// this should really be blocked earlier though | ||
esLinkRecord.execute.call(execContext); | ||
execute.call(context); | ||
} | ||
catch(err) { | ||
return err; | ||
catch (e) { | ||
return e; | ||
} | ||
} | ||
function namespaceEvaluate(namespace) { | ||
function nsEvaluate (ns) { | ||
try { | ||
Module.evaluate(namespace); | ||
Module.evaluate(ns); | ||
} | ||
catch(err) { | ||
return err; | ||
catch (e) { | ||
return e; | ||
} | ||
} | ||
function traceLoadRecord(loader, load, seen) { | ||
// its up to dynamic instantiate layers to ensure their own traces are present | ||
if (load instanceof ModuleNamespace) | ||
return; | ||
seen.push(load); | ||
if (!load.esLinkRecord || load.esLinkRecord.dependencies.length && !load.esLinkRecord.depMap) | ||
throw new Error('Tracing error, ensure loader.trace is set before loading begins'); | ||
loader.loads[load.key] = { | ||
key: load.key, | ||
dependencies: load.esLinkRecord.dependencies, | ||
depMap: load.esLinkRecord.depMap || {}, | ||
metadata: load.metadata | ||
}; | ||
load.esLinkRecord.dependencyInstantiations.forEach(function(dep) { | ||
if (seen.indexOf(dep) === -1) | ||
traceLoadRecord(loader, dep, seen); | ||
}); | ||
} |
@@ -5,6 +5,6 @@ import { isNode } from './common.js'; | ||
* Optimized URL normalization assuming a syntax-valid URL parent | ||
*/ | ||
export function resolveUrlToParentIfNotPlain(relUrl, parentUrl) { | ||
*/ | ||
export function resolveUrlToParentIfNotPlain (relUrl, parentUrl) { | ||
function throwResolveError() { | ||
function throwResolveError () { | ||
throw new RangeError('Unable to resolve "' + relUrl + '" to ' + parentUrl); | ||
@@ -67,3 +67,3 @@ } | ||
} | ||
// join together and split for removal of .. and . segments | ||
@@ -107,3 +107,3 @@ // looping the string instead of anything fancy for perf reasons | ||
throwResolveError(); | ||
// trailing . or .. segment | ||
@@ -121,7 +121,7 @@ if (i === segmented.length) | ||
output.push(segmented.substr(segmentIndex, segmented.length - segmentIndex)); | ||
return parentUrl.substr(0, parentUrl.length - pathname.length) + output.join(''); | ||
} | ||
// plain name -> return undefined | ||
} | ||
} |
{ | ||
"name": "es-module-loader", | ||
"description": "An ES Module Loader shim", | ||
"version": "1.2.1", | ||
"version": "1.3.0", | ||
"homepage": "https://github.com/ModuleLoader/es-module-loader", | ||
@@ -24,5 +24,5 @@ "author": { | ||
"devDependencies": { | ||
"babel-cli": "^6.11.4", | ||
"babel-plugin-transform-async-to-generator": "^6.8.0", | ||
"babel-plugin-transform-es2015-modules-systemjs": "^6.12.0", | ||
"babel-cli": "^6.16.0", | ||
"babel-plugin-transform-async-to-generator": "^6.16.0", | ||
"babel-plugin-transform-es2015-modules-systemjs": "^6.14.0", | ||
"benchmark": "^2.1.1", | ||
@@ -29,0 +29,0 @@ "mocha": "^3.0.2", |
105
README.md
# ES Module Loader Polyfill [![Build Status][travis-image]][travis-url] | ||
Provides a polyfill and [low-level API](#loader-hooks) for the [WhatWG loader spec](https://github.com/whatwg/loader) to create a custom module loaders. | ||
Provides [low-level hooks](#loader-hooks) for creating ES module loaders, roughly based on the API of the [WhatWG loader spec](https://github.com/whatwg/loader), | ||
but with [various adjustments](#spec-differences) to match the current proposals for the HTML modules specification and [NodeJS ES module adoption](https://github.com/nodejs/node/issues/8866). | ||
Supports the [System.register module format](https://github.com/ModuleLoader/es-module-loader/blob/master/docs/system-register.md) to provide identical module loading semantics as ES modules in environments today. | ||
Supports the [System.register](docs/system-register.md) module format to provide exact module loading semantics for ES modules in environments today. In addition, support for the [System.registerDynamic](docs/system-register-dynamic.md) is provided to allow the linking | ||
of module graphs consisting of inter-dependent ES modules and CommonJS modules with their respective semantics retained. | ||
ES6 Module Loader Polyfill, the previous version of this project built to the [outdated ES6 loader specification](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts#august_24_2014_draft_rev_27) is available at the [0.17 branch](https://github.com/ModuleLoader/es-module-loader/tree/0.17). | ||
This project aims to provide a [highly performant](#performance), minimal, unopinionated loader API on top of which custom loaders [can easily be built](#creating-a-loader). See the [spec differences](#spec-differences) section for a more detailed listing of the tradeoffs made. | ||
ES6 Module Loader Polyfill, the previous version of this project was built to the [outdated ES6 loader specification](http://wiki.ecmascript.org/doku.php?id=harmony:specification_drafts#august_24_2014_draft_rev_27) and can still be found at the [0.17 branch](https://github.com/ModuleLoader/es-module-loader/tree/0.17). | ||
### Module Loader Examples | ||
@@ -17,3 +21,3 @@ | ||
- [Node ES Module Loader](https://github.com/ModuleLoader/node-es-module-loader) | ||
Allows loading ES modules with CommonJS interop in Node via `node-esml module/path.js` in line with the current Node | ||
Allows loading ES modules with CommonJS interop in Node via `node-esml module/path.js` in line with the current Node | ||
plans for implementing ES modules. Used to run the tests and benchmarks in this project. | ||
@@ -34,10 +38,10 @@ | ||
The minimal polyfill loader is provided in `core/loader-polyfill.js`. On top of this the main API file is | ||
'core/register-loader.js'` which provides the base loader class. | ||
The minimal [polyfill loader API](@base-loader-polyfill-api) is provided in `core/loader-polyfill.js`. On top of this main API file is | ||
`core/register-loader.js` which provides a base loader class with the non-spec `System.register` and `System.registerDynamic` support to enable the exact | ||
linking semantics. | ||
Helper functions are available in `core/resolve.js`, `core/common.js`, `core/fetch.js` and everything that is exported can be considered | ||
Helper functions are available in `core/resolve.js` and `core/common.js`. Everything that is exported can be considered | ||
part of the publicly versioned API of this project. | ||
Any tool can be used to build the loader distribution file from these core modules - [Rollup](http://rollupjs.org) is used to do these builds in the example loaders above, | ||
provided by the `rollup.config.js` file in the example loader repos listed above. | ||
Any tool can be used to build the loader distribution file from these core modules - [Rollup](http://rollupjs.org) is used to do these builds in the example loaders above, provided by the `rollup.config.js` file in the example loader repos listed above. | ||
@@ -63,3 +67,2 @@ ### Loader Hooks | ||
[RegisterLoader.normalize](key, parentKey, metadata) { | ||
// parent normalize is sync, providing relative normalization only | ||
var relativeResolved = super[RegisterLoader.normalize](key, parentKey, metadata) || key; | ||
@@ -81,14 +84,12 @@ return relativeResolved; | ||
#### Normalize Hook | ||
Relative normalization of the form `./x` is already performed using the internal resolver in `core/resolve.js` | ||
so that the key provided into normalize will never be a relative URL - it will either be a plain / bare name | ||
or an absolute URL. | ||
The return value of `normalize` is the final key that is set in the registry (available and iterable as per the spec | ||
at `loader.registry`). | ||
The default normalization provided (`super[RegisterLoader.normalize]` above) is based on the principles of the HTML specification for modules, whereby _plain module names_ that are not valid URLs, and not starting with `./`, `../` or `/` return `undefined`. | ||
So for example `lodash` will return `undefined`, while `./x` will resolve to `[baseURI]/x`. In NodeJS a `file:///` URL is used for the baseURI. | ||
#### Instantiate Hook | ||
##### Instantiating ES Modules via System.register | ||
##### 1. Instantiating ES Modules via System.register | ||
@@ -121,13 +122,18 @@ When instantiate returns `undefined`, it is assumed that the module key has already been registered through a | ||
##### Instantiating Dynamic / Legacy Modules via ModuleNamespace | ||
The `key` and `contextKey` provided to `register` or `processRegisterContext` must be the exact key to use in the registry. If not, the module will not be detected correctly. | ||
Legacy module formats are not transpiled into `System.register`, rather they need to be executed according to their own semantics. | ||
##### 2. Instantiating Legacy Modules via System.registerDynamic | ||
The instantiate can handle its own execution pipeline for these legacy modules (like calling out to the Node require in the node-es-module-loader). | ||
This is identical to the `System.register` process above, only running `loader.registerDynamic` instead of `loader.register`. | ||
Having created a module instance, we wrap it in a `ModuleNamespace` object and can return that directly from instantiate: | ||
For more information on the `System.registerDynamic` format [see the format explanation](docs/system-register-dynamic.md). | ||
##### 3. Instantiating Dynamic Modules via ModuleNamespace | ||
If the exact module definition is already known, or loaded through another method (like calling out fully to the Node require in the node-es-module-loader), | ||
then the direct module namespace value can be returned from instantiate: | ||
```javascript | ||
import { InternalModuleNamespace } from 'es-module-loader/core/loader-polyfill.js' | ||
import { ModuleNamespace } from 'es-module-loader/core/loader-polyfill.js' | ||
@@ -139,12 +145,43 @@ // ... | ||
return new InternalModuleNamespace({ default: module }); | ||
return new ModuleNamespace({ default: module, customExport: 'value' }); | ||
} | ||
``` | ||
Using these two types of return values, we can thus recreate ES module semantics interacting with legacy module formats. | ||
Using these three types of return values for instantiate, we can thus recreate ES module semantics interacting with legacy module formats. | ||
Note that `InternalModuleNamespace` is not provided in the WhatWG loader specification - the specification actually uses a `Module.Status` constructor. | ||
We've chosen to take the route of implementing a custom private method over the spec, until that spec work can be fully stabilized, instead of having | ||
Note that `ModuleNamespace` is not specified in the WhatWG loader specification - the specification actually uses a `Module.Status` constructor. | ||
A custom private constructor is used over the spec until there is a stable proposal instead of having | ||
to track small changes of this spec API over major versions of this project. | ||
### Base Loader Polyfill API | ||
The `Loader` and `Module` classes in `core/loader-polyfill.js` provide the basic spec API method shells for a loader instance `loader`: | ||
- *`new Loader(baseKey)`*: Instantiate a new `loader` instance, with the given `baseKey` as the default parentKey for normalizations. | ||
Defaults to environment baseURI detection in NodeJS and browsers. | ||
- *`loader.import(key [, parentKey])`*: Promise for importing and executing a given module, returning its module instance. | ||
- *`loader.resolve(key [, parentKey])`*: Promise for resolving the idempotent fully-normalized string key for a module. | ||
- *`new Module(bindings)`*: Creates a new module namespace object instance for the given bindings object. The iterable properties | ||
of the bindings object are created as getters returning the corresponding values from the bindings object. | ||
- *`loader.registry.set(resolvedKey, namespace)`*: Set a module namespace into the registry. | ||
- *`loader.registry.get(resolvedKey)`*: Get a module namespace (if any) from the registry. | ||
- *`loader.registry.has(resolvedKey)`*: Boolean indicating whether the given key is present in the registry. | ||
- *`loader.registry.delete(resolvedKey)``*: Removes the given module from the registry (if any), returning true or false. | ||
- *`loader.registry.keys`*: Function returning the keys iterator for the registry. | ||
- *`loader.registry.values`*: Function returning the values iterator for the registry. | ||
- *`loader.registry.entries`*: Function returning the entries iterator for the registry (keys and values). | ||
- *`loader.registry[Symbol.iterator]`*: In supported environments, provides registry entries iteration. | ||
### Performance | ||
A performance comparison loading System.register modules is provided in the `bench` folder comparing times between | ||
the minimal [System Register Loader](https://github.com/ModuleLoader/system-register-loader) and SystemJS (which is built to the previous loader polyfill): | ||
| Test | SystemJS | ES Module Loader 1.2 | | ||
| ----------------------------------------- |:-----------:| :-------------------:| | ||
| Importing multiple trees at the same time | 147 ops/sec | 705 ops/sec | | ||
| Importing a deep tree of modules | 225 ops/sec | 4,713 ops/sec | | ||
| Importing a single module with deps | 153 ops/sec | 9,652 ops/sec | | ||
| Importing a single module without deps | 119 ops/sec | 16,279 ops/sec | | ||
### Tracing API | ||
@@ -167,6 +204,2 @@ | ||
Instantiate functions that return an `InternalModuleNamespace` instance directly are not included in the trace registry. | ||
Custom loaders that want to share the same trace format, should populate the trace themselves using their internal knowledge of the legacy module dependency information. | ||
### Spec Differences | ||
@@ -177,14 +210,14 @@ | ||
Error handling is implemented as in the HTML specification for module loading, such that rejections reject the current load tree, but | ||
Error handling is implemented as in the HTML specification for module loading, such that rejections reject the current in-progress load trees, but | ||
are immediately removed from the registry to allow further loads to retry loading. | ||
- Instead of storing a registry of ModuleStatus objects, we store a registry of Module Namespace objects. The reason for this is that asynchronous rejection of registry entries as a source of truth leads to partial inconsistent rejection states | ||
- A direct `ModuleNamespace` constructor is provided over the `Module` mutator proposal in the WhatWG specification. | ||
Instead of storing a registry of ModuleStatus objects, we then store a registry of Module Namespace objects. The reason for this is that asynchronous rejection of registry entries as a source of truth leads to partial inconsistent rejection states | ||
(it is possible for the tick between the rejection of one load and its parent to have to deal with an overlapping in-progress tree), | ||
so in order to have a predictable load error rejection process, loads are only stored in the registry as fully-linked Namespace objects | ||
and not ModuleStatus objects as promises for Namespace objects (Module.evaluate is still supported though). | ||
- `Loader` and `Module` are available as named exports from `core/loader-polyfill.js` but are not by default exported to the `global.Reflect` object. | ||
- `Loader` is available as a named export from `core/loader-polyfill.js` but is not by default exported to the `global.Reflect` object. | ||
This is to allow individual loader implementations to determine their own impact on the environment. | ||
- A constructor argument is added to the loader that takes the environment baseKey to be used as the default normalization parent. | ||
- An internal `Loader.prototype[Loader.instantiate]` hook is used as well as the `Loader.prototype[Loader.resolve]` hook | ||
in order to ensure that uses of `loader.resolve` do not have to result in module loading and execution, as discussed in https://github.com/whatwg/loader/issues/147#issuecomment-230407764. | ||
- A constructor argument is added to the loader that takes the environment `baseKey` to be used as the default normalization parent. | ||
- The [WhatWG reduced specification proposal](https://github.com/whatwg/loader/issues/147) is to remove the loader hooks and simply have a single `resolve` hook, which could then set a module into the registry using the `registry.set` API as a side-effect to allow custom interception. As discussed in https://github.com/whatwg/loader/issues/147#issuecomment-230407764, this may cause unwanted execution of modules when only resolution is needed via `loader.resolve`, so the internal approach taken here is to still consider separate `resolve` and `instantiate` hooks in `core/loader-polyfill.js`. | ||
@@ -191,0 +224,0 @@ ## License |
@@ -44,8 +44,4 @@ import assert from 'assert'; | ||
var evaluated = false; | ||
var mutator; | ||
var module = new Module({ | ||
a: { value: 'asdf' } | ||
}, function(_mutator) { | ||
mutator = _mutator; | ||
}, function() { | ||
var mutator = { a: 'asdf' }; | ||
var module = new Module(mutator, function() { | ||
evaluated = true; | ||
@@ -55,4 +51,4 @@ mutator.a = 'b'; | ||
assert.equal(module.a, 'asdf'); | ||
assert.equal(module.a, undefined); | ||
Module.evaluate(module); | ||
@@ -63,2 +59,2 @@ assert(evaluated); | ||
}); | ||
}); | ||
}); |
import assert from 'assert'; | ||
import path from 'path'; | ||
import Module from 'module'; | ||
import SystemRegisterLoader from './fixtures/system-register-loader.js'; | ||
@@ -7,3 +8,3 @@ import { pathToFileUrl, fileUrlToPath } from '../core/common.js'; | ||
describe('System Register Loader', function() { | ||
var loader = new SystemRegisterLoader(path.resolve('test/fixtures/register-modules') + path.sep); | ||
var loader = new SystemRegisterLoader(path.resolve('test/fixtures') + path.sep); | ||
@@ -17,3 +18,3 @@ describe('Simple tests', function() { | ||
it('Should import a module', async function() { | ||
var m = await loader.import('./no-imports.js'); | ||
var m = await loader.import('./register-modules/no-imports.js'); | ||
assert(m); | ||
@@ -24,4 +25,4 @@ assert.equal(m.asdf, 'asdf'); | ||
it('Should import a module cached', async function() { | ||
var m1 = await loader.import('./no-imports.js'); | ||
var m2 = await loader.import('./no-imports.js'); | ||
var m1 = await loader.import('./register-modules/no-imports.js'); | ||
var m2 = await loader.import('./register-modules/no-imports.js'); | ||
assert.equal(m1.asdf, 'asdf'); | ||
@@ -32,3 +33,3 @@ assert.equal(m1, m2); | ||
it('should import an es module with its dependencies', async function() { | ||
var m = await loader.import('./es6-withdep.js'); | ||
var m = await loader.import('./register-modules/es6-withdep.js'); | ||
assert.equal(m.p, 'p'); | ||
@@ -38,3 +39,3 @@ }); | ||
it('should import without bindings', async function() { | ||
var m = await loader.import('./direct.js'); | ||
var m = await loader.import('./register-modules/direct.js'); | ||
assert(!!m); | ||
@@ -44,3 +45,3 @@ }); | ||
it('should support various es syntax', async function() { | ||
var m = await loader.import('./es6-file.js'); | ||
var m = await loader.import('./register-modules/es6-file.js'); | ||
@@ -63,3 +64,3 @@ assert.equal(typeof m.q, 'function'); | ||
it('should resolve various import syntax', async function() { | ||
var m = await loader.import('./import.js'); | ||
var m = await loader.import('./register-modules/import.js'); | ||
assert.equal(typeof m.a, 'function'); | ||
@@ -74,3 +75,3 @@ assert.equal(m.b, 4); | ||
it('should support __moduleName', async function() { | ||
var m = await loader.import('./moduleName.js'); | ||
var m = await loader.import('./register-modules/moduleName.js'); | ||
assert.equal(m.name, pathToFileUrl(path.resolve('test/fixtures/register-modules/moduleName.js'))); | ||
@@ -84,4 +85,4 @@ }); | ||
it('should resolve circular dependencies', async function() { | ||
var m1 = await loader.import('./circular1.js'); | ||
var m2 = await loader.import('./circular2.js'); | ||
var m1 = await loader.import('./register-modules/circular1.js'); | ||
var m2 = await loader.import('./register-modules/circular2.js'); | ||
@@ -98,5 +99,4 @@ | ||
// pending https://github.com/babel/babel/pull/3650 | ||
it.skip('should update circular dependencies', async function() { | ||
var m = await loader.import('./even.js'); | ||
it('should update circular dependencies', async function() { | ||
var m = await loader.import('./register-modules/even.js'); | ||
assert.equal(m.counter, 1); | ||
@@ -113,3 +113,3 @@ assert(m.even(10)); | ||
async function assertLoadOrder(module, exports) { | ||
var m = await loader.import('./' + module); | ||
var m = await loader.import('./register-modules/' + module); | ||
exports.forEach(function(name) { | ||
@@ -151,3 +151,3 @@ assert.equal(m[name], name); | ||
it('should resolve different export syntax', async function() { | ||
var m = await loader.import('./export.js'); | ||
var m = await loader.import('./register-modules/export.js'); | ||
assert.equal(m.p, 5); | ||
@@ -163,3 +163,3 @@ assert.equal(typeof m.foo, 'function'); | ||
it('should resolve "export default"', async function() { | ||
var m = await loader.import('./export-default.js'); | ||
var m = await loader.import('./register-modules/export-default.js'); | ||
assert.equal(m.default(), 'test'); | ||
@@ -169,3 +169,3 @@ }); | ||
it('should support simple re-exporting', async function() { | ||
var m = await loader.import('./reexport1.js'); | ||
var m = await loader.import('./register-modules/reexport1.js'); | ||
assert.equal(m.p, 5); | ||
@@ -175,4 +175,4 @@ }); | ||
it('should support re-exporting binding', async function() { | ||
await loader.import('./reexport-binding.js'); | ||
var m = await loader.import('./rebinding.js'); | ||
await loader.import('./register-modules/reexport-binding.js'); | ||
var m = await loader.import('./register-modules/rebinding.js'); | ||
assert.equal(m.p, 4); | ||
@@ -182,3 +182,3 @@ }); | ||
it('should support re-exporting with a new name', async function() { | ||
var m = await loader.import('./reexport2.js'); | ||
var m = await loader.import('./register-modules/reexport2.js'); | ||
assert.equal(m.q, 4); | ||
@@ -189,3 +189,3 @@ assert.equal(m.z, 5); | ||
it('should support re-exporting', async function() { | ||
var m = await loader.import('./export-star.js'); | ||
var m = await loader.import('./register-modules/export-star.js'); | ||
assert.equal(m.foo, 'foo'); | ||
@@ -196,3 +196,3 @@ assert.equal(m.bar, 'bar'); | ||
it.skip('should support re-exporting overwriting', async function() { | ||
var m = await loader.import('./export-star2.js'); | ||
var m = await loader.import('./register-modules/export-star2.js'); | ||
assert.equal(m.bar, 'bar'); | ||
@@ -205,3 +205,3 @@ assert.equal(typeof m.foo, 'function'); | ||
var testPath = fileUrlToPath(loader.key); | ||
var testPath = fileUrlToPath(loader.key) + 'register-modules/'; | ||
@@ -219,13 +219,13 @@ async function getImportError(module) { | ||
it('should throw if on syntax error', async function() { | ||
var err = await getImportError('./main.js'); | ||
assert.equal(err, 'Error: dep error\n Evaluating ' + testPath + 'deperror.js\n Evaluating ' + testPath + 'main.js\n Loading ./main.js'); | ||
var err = await getImportError('./register-modules/main.js'); | ||
assert.equal(err, 'Error: dep error\n Evaluating ' + testPath + 'deperror.js\n Evaluating ' + testPath + 'main.js\n Loading ./register-modules/main.js'); | ||
}); | ||
it('should throw what the script throws', async function() { | ||
var err = await getImportError('./deperror.js'); | ||
assert.equal(err, 'Error: dep error\n Evaluating ' + testPath + 'deperror.js\n Loading ./deperror.js'); | ||
var err = await getImportError('./register-modules/deperror.js'); | ||
assert.equal(err, 'Error: dep error\n Evaluating ' + testPath + 'deperror.js\n Loading ./register-modules/deperror.js'); | ||
}); | ||
it('404 error', async function() { | ||
var err = await getImportError('./load-non-existent.js'); | ||
var err = await getImportError('./register-modules/load-non-existent.js'); | ||
var lines = err.split('\n '); | ||
@@ -236,3 +236,3 @@ assert(lines[0].startsWith('Error: ')); | ||
assert.equal(lines[2], 'Loading ' + testPath + 'load-non-existent.js'); | ||
assert.equal(lines[3], 'Loading ./load-non-existent.js'); | ||
assert.equal(lines[3], 'Loading ./register-modules/load-non-existent.js'); | ||
}); | ||
@@ -242,2 +242,18 @@ | ||
describe('Register dynamic', function () { | ||
it('should load a System.registerDynamic module', async function () { | ||
var m = await loader.import('./dynamic-modules/basic-exports.js'); | ||
assert.equal(m.default(), 'ok'); | ||
assert.equal(m.named, 'name!'); | ||
}); | ||
it('should load mixed bundles of register and registerDynamic', async function() { | ||
new Module().require(path.resolve(fileUrlToPath(loader.key), 'dynamic-modules/mixed-bundle.js')); | ||
var m = await loader.import('tree/first'); | ||
assert.equal(m.p, 5); | ||
assert.equal(m.q, 4); | ||
assert.equal(m.a.is, 'amd'); | ||
}); | ||
}); | ||
}); |
@@ -12,3 +12,3 @@ import RegisterLoader from '../../core/register-loader.js'; | ||
*/ | ||
function SystemRegisterLoader(baseKey) { | ||
function SystemRegisterLoader (baseKey) { | ||
baseKey = resolveUrlToParentIfNotPlain(baseKey || (isNode ? process.cwd() : '.'), baseURI) || baseKey; | ||
@@ -18,8 +18,10 @@ RegisterLoader.call(this, baseKey); | ||
var loader = this; | ||
// ensure System.register is available | ||
global.System = global.System || {}; | ||
if (typeof global.System.register == 'function') | ||
if (typeof global.System.register === 'function') | ||
var prevRegister = global.System.register; | ||
global.System.register = function() { | ||
if (typeof global.System.registerDynamic === 'function') | ||
var prevRegisterDynamic = global.System.registerDynamic; | ||
global.System.register = function () { | ||
loader.register.apply(loader, arguments); | ||
@@ -29,2 +31,7 @@ if (prevRegister) | ||
}; | ||
global.System.registerDynamic = function () { | ||
loader.registerDynamic.apply(loader, arguments); | ||
if (prevRegisterDynamic) | ||
prevRegisterDynamic.apply(this, arguments); | ||
} | ||
} | ||
@@ -35,3 +42,3 @@ SystemRegisterLoader.prototype = Object.create(RegisterLoader.prototype); | ||
// so we just need to do plain name detect to throw as in the WhatWG spec | ||
SystemRegisterLoader.prototype[RegisterLoader.normalize] = function(key, parent, metadata) { | ||
SystemRegisterLoader.prototype[RegisterLoader.normalize] = function (key, parent, metadata) { | ||
var resolved = RegisterLoader.prototype.normalize.call(this, key, parent, metadata); | ||
@@ -47,10 +54,10 @@ if (!resolved) | ||
// so we load the module name as a URL, and expect that to run System.register | ||
SystemRegisterLoader.prototype[RegisterLoader.instantiate] = function(key, metadata) { | ||
SystemRegisterLoader.prototype[RegisterLoader.instantiate] = function (key, metadata) { | ||
var thisLoader = this; | ||
return new Promise(function(resolve, reject) { | ||
return new Promise(function (resolve, reject) { | ||
if (isNode) | ||
Promise.resolve(fs || (fs = typeof require !== 'undefined' ? require('fs') : loader.import('fs').then(function(m){ return m.default }))) | ||
.then(function(fs) { | ||
fs.readFile(fileUrlToPath(key), function(err, source) { | ||
.then(function (fs) { | ||
fs.readFile(fileUrlToPath(key), function (err, source) { | ||
if (err) | ||
@@ -70,3 +77,3 @@ return reject(err); | ||
else if (isBrowser) | ||
scriptLoad(key, function() { | ||
scriptLoad(key, function () { | ||
thisLoader.processRegisterContext(key); | ||
@@ -80,3 +87,3 @@ resolve(); | ||
function scriptLoad(src, resolve, reject) { | ||
function scriptLoad (src, resolve, reject) { | ||
var script = document.createElement('script'); | ||
@@ -98,3 +105,3 @@ script.type = 'text/javascript'; | ||
function error(err) { | ||
function error (err) { | ||
cleanup(); | ||
@@ -104,3 +111,3 @@ reject(new Error('Fetching ' + src)); | ||
function cleanup() { | ||
function cleanup () { | ||
script.removeEventListener('load', load, false); | ||
@@ -107,0 +114,0 @@ script.removeEventListener('error', error, false); |
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Dynamic require
Supply chain riskDynamic require can indicate the package is performing dangerous or unsafe dynamic code execution.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
250019
64
8243
221
7