mirror of
https://github.com/systemjs/systemjs.git
synced 2026-01-18 14:53:14 +00:00
* Reproduce issue #2286. * Making test more clear * Fix * Self review * Add load.p * possible parent fix * optional p * parent restriction * Fix * Update src/system-core.js * another variation Co-authored-by: Guy Bedford <guybedford@gmail.com>
309 lines
8.4 KiB
JavaScript
309 lines
8.4 KiB
JavaScript
/*
|
|
* SystemJS Core
|
|
*
|
|
* Provides
|
|
* - System.import
|
|
* - System.register support for
|
|
* live bindings, function hoisting through circular references,
|
|
* reexports, dynamic import, import.meta.url, top-level await
|
|
* - System.getRegister to get the registration
|
|
* - Symbol.toStringTag support in Module objects
|
|
* - Hookable System.createContext to customize import.meta
|
|
* - System.onload(err, id, deps) handler for tracing / hot-reloading
|
|
*
|
|
* Core comes with no System.prototype.resolve or
|
|
* System.prototype.instantiate implementations
|
|
*/
|
|
import { global, hasSymbol } from './common.js';
|
|
import { errMsg } from './err-msg.js';
|
|
export { systemJSPrototype, REGISTRY }
|
|
|
|
var toStringTag = hasSymbol && Symbol.toStringTag;
|
|
var REGISTRY = hasSymbol ? Symbol() : '@';
|
|
|
|
function SystemJS () {
|
|
this[REGISTRY] = {};
|
|
}
|
|
|
|
var systemJSPrototype = SystemJS.prototype;
|
|
|
|
systemJSPrototype.import = function (id, parentUrl) {
|
|
var loader = this;
|
|
return Promise.resolve(loader.prepareImport())
|
|
.then(function() {
|
|
return loader.resolve(id, parentUrl);
|
|
})
|
|
.then(function (id) {
|
|
var load = getOrCreateLoad(loader, id);
|
|
return load.C || topLevelLoad(loader, load);
|
|
});
|
|
};
|
|
|
|
// Hookable createContext function -> allowing eg custom import meta
|
|
systemJSPrototype.createContext = function (parentId) {
|
|
var loader = this;
|
|
return {
|
|
url: parentId,
|
|
resolve: function (id, parentUrl) {
|
|
return Promise.resolve(loader.resolve(id, parentUrl || parentId));
|
|
}
|
|
};
|
|
};
|
|
|
|
// onLoad(err, id, deps) provided for tracing / hot-reloading
|
|
if (!process.env.SYSTEM_PRODUCTION)
|
|
systemJSPrototype.onload = function () {};
|
|
function loadToId (load) {
|
|
return load.id;
|
|
}
|
|
function triggerOnload (loader, load, err, isErrSource) {
|
|
loader.onload(err, load.id, load.d && load.d.map(loadToId), !!isErrSource);
|
|
if (err)
|
|
throw err;
|
|
}
|
|
|
|
var lastRegister;
|
|
systemJSPrototype.register = function (deps, declare) {
|
|
lastRegister = [deps, declare];
|
|
};
|
|
|
|
/*
|
|
* getRegister provides the last anonymous System.register call
|
|
*/
|
|
systemJSPrototype.getRegister = function () {
|
|
var _lastRegister = lastRegister;
|
|
lastRegister = undefined;
|
|
return _lastRegister;
|
|
};
|
|
|
|
export function getOrCreateLoad (loader, id, firstParentUrl) {
|
|
var load = loader[REGISTRY][id];
|
|
if (load)
|
|
return load;
|
|
|
|
var importerSetters = [];
|
|
var ns = Object.create(null);
|
|
if (toStringTag)
|
|
Object.defineProperty(ns, toStringTag, { value: 'Module' });
|
|
|
|
var instantiatePromise = Promise.resolve()
|
|
.then(function () {
|
|
return loader.instantiate(id, firstParentUrl);
|
|
})
|
|
.then(function (registration) {
|
|
if (!registration)
|
|
throw Error(errMsg(2, process.env.SYSTEM_PRODUCTION ? id : 'Module ' + id + ' did not instantiate'));
|
|
function _export (name, value) {
|
|
// note if we have hoisted exports (including reexports)
|
|
load.h = true;
|
|
var changed = false;
|
|
if (typeof name === 'string') {
|
|
if (!(name in ns) || ns[name] !== value) {
|
|
ns[name] = value;
|
|
changed = true;
|
|
}
|
|
}
|
|
else {
|
|
for (var p in name) {
|
|
var value = name[p];
|
|
if (!(p in ns) || ns[p] !== value) {
|
|
ns[p] = value;
|
|
changed = true;
|
|
}
|
|
}
|
|
|
|
if (name.__esModule) {
|
|
ns.__esModule = name.__esModule;
|
|
}
|
|
}
|
|
if (changed)
|
|
for (var i = 0; i < importerSetters.length; i++) {
|
|
var setter = importerSetters[i];
|
|
if (setter) setter(ns);
|
|
}
|
|
return value;
|
|
}
|
|
var declared = registration[1](_export, registration[1].length === 2 ? {
|
|
import: function (importId) {
|
|
return loader.import(importId, id);
|
|
},
|
|
meta: loader.createContext(id)
|
|
} : undefined);
|
|
load.e = declared.execute || function () {};
|
|
return [registration[0], declared.setters || []];
|
|
}, function (err) {
|
|
load.e = null;
|
|
load.er = err;
|
|
if (!process.env.SYSTEM_PRODUCTION) triggerOnload(loader, load, err, true);
|
|
throw err;
|
|
});
|
|
|
|
var linkPromise = instantiatePromise
|
|
.then(function (instantiation) {
|
|
return Promise.all(instantiation[0].map(function (dep, i) {
|
|
var setter = instantiation[1][i];
|
|
return Promise.resolve(loader.resolve(dep, id))
|
|
.then(function (depId) {
|
|
var depLoad = getOrCreateLoad(loader, depId, id);
|
|
// depLoad.I may be undefined for already-evaluated
|
|
return Promise.resolve(depLoad.I)
|
|
.then(function () {
|
|
if (setter) {
|
|
depLoad.i.push(setter);
|
|
// only run early setters when there are hoisted exports of that module
|
|
// the timing works here as pending hoisted export calls will trigger through importerSetters
|
|
if (depLoad.h || !depLoad.I)
|
|
setter(depLoad.n);
|
|
}
|
|
return depLoad;
|
|
});
|
|
});
|
|
}))
|
|
.then(function (depLoads) {
|
|
load.d = depLoads;
|
|
});
|
|
});
|
|
if (!process.env.SYSTEM_BROWSER)
|
|
linkPromise.catch(function () {});
|
|
|
|
// Capital letter = a promise function
|
|
return load = loader[REGISTRY][id] = {
|
|
id: id,
|
|
// importerSetters, the setters functions registered to this dependency
|
|
// we retain this to add more later
|
|
i: importerSetters,
|
|
// module namespace object
|
|
n: ns,
|
|
|
|
// instantiate
|
|
I: instantiatePromise,
|
|
// link
|
|
L: linkPromise,
|
|
// whether it has hoisted exports
|
|
h: false,
|
|
|
|
// On instantiate completion we have populated:
|
|
// dependency load records
|
|
d: undefined,
|
|
// execution function
|
|
e: undefined,
|
|
|
|
// On execution we have populated:
|
|
// the execution error if any
|
|
er: undefined,
|
|
// in the case of TLA, the execution promise
|
|
E: undefined,
|
|
|
|
// On execution, L, I, E cleared
|
|
|
|
// Promise for top-level completion
|
|
C: undefined,
|
|
|
|
// parent instantiator / executor
|
|
p: undefined
|
|
};
|
|
}
|
|
|
|
function instantiateAll (loader, load, parent, loaded) {
|
|
if (!loaded[load.id]) {
|
|
loaded[load.id] = true;
|
|
// load.L may be undefined for already-instantiated
|
|
return Promise.resolve(load.L)
|
|
.then(function () {
|
|
if (!load.p || load.p.e === null)
|
|
load.p = parent;
|
|
return Promise.all(load.d.map(function (dep) {
|
|
return instantiateAll(loader, dep, parent, loaded);
|
|
}));
|
|
})
|
|
.catch(function (err) {
|
|
if (load.er)
|
|
throw err;
|
|
load.e = null;
|
|
if (!process.env.SYSTEM_PRODUCTION) triggerOnload(loader, load, err, false);
|
|
throw err;
|
|
});
|
|
}
|
|
}
|
|
|
|
function topLevelLoad (loader, load) {
|
|
return load.C = instantiateAll(loader, load, load, {})
|
|
.then(function () {
|
|
return postOrderExec(loader, load, {});
|
|
})
|
|
.then(function () {
|
|
return load.n;
|
|
});
|
|
}
|
|
|
|
// the closest we can get to call(undefined)
|
|
var nullContext = Object.freeze(Object.create(null));
|
|
|
|
// returns a promise if and only if a top-level await subgraph
|
|
// throws on sync errors
|
|
function postOrderExec (loader, load, seen) {
|
|
if (seen[load.id])
|
|
return;
|
|
seen[load.id] = true;
|
|
|
|
if (!load.e) {
|
|
if (load.er)
|
|
throw load.er;
|
|
if (load.E)
|
|
return load.E;
|
|
return;
|
|
}
|
|
|
|
// deps execute first, unless circular
|
|
var depLoadPromises;
|
|
load.d.forEach(function (depLoad) {
|
|
try {
|
|
var depLoadPromise = postOrderExec(loader, depLoad, seen);
|
|
if (depLoadPromise)
|
|
(depLoadPromises = depLoadPromises || []).push(depLoadPromise);
|
|
}
|
|
catch (err) {
|
|
load.e = null;
|
|
load.er = err;
|
|
if (!process.env.SYSTEM_PRODUCTION) triggerOnload(loader, load, err, false);
|
|
throw err;
|
|
}
|
|
});
|
|
if (depLoadPromises)
|
|
return Promise.all(depLoadPromises).then(doExec);
|
|
|
|
return doExec();
|
|
|
|
function doExec () {
|
|
try {
|
|
var execPromise = load.e.call(nullContext);
|
|
if (execPromise) {
|
|
execPromise = execPromise.then(function () {
|
|
load.C = load.n;
|
|
load.E = null; // indicates completion
|
|
if (!process.env.SYSTEM_PRODUCTION) triggerOnload(loader, load, null, true);
|
|
}, function (err) {
|
|
load.er = err;
|
|
load.E = null;
|
|
if (!process.env.SYSTEM_PRODUCTION) triggerOnload(loader, load, err, true);
|
|
throw err;
|
|
});
|
|
return load.E = execPromise;
|
|
}
|
|
// (should be a promise, but a minify optimization to leave out Promise.resolve)
|
|
load.C = load.n;
|
|
load.L = load.I = undefined;
|
|
}
|
|
catch (err) {
|
|
load.er = err;
|
|
throw err;
|
|
}
|
|
finally {
|
|
load.e = null;
|
|
if (!process.env.SYSTEM_PRODUCTION) triggerOnload(loader, load, load.er, true);
|
|
}
|
|
}
|
|
}
|
|
|
|
global.System = new SystemJS();
|