new normalization algorithm

This commit is contained in:
guybedford 2015-05-06 20:58:29 +02:00
parent a93a085d47
commit a1f57f79bf
5 changed files with 204 additions and 105 deletions

View File

@ -59,7 +59,7 @@ dist/system.src.js: lib/*.js $(ESML)/*.js
$(ESML)/transpiler.js \
lib/global-eval.js \
lib/core.js \
lib/config.js \
lib/misc.js \
lib/scriptLoader.js \
lib/meta.js \
lib/register.js \
@ -90,7 +90,7 @@ dist/system-prod.src.js: lib/*.js $(ESML)/*.js
$(ESML)/dynamic-only.js \
$(ESML)/system.js \
lib/core.js \
lib/config.js \
lib/misc.js \
lib/scriptLoader.js \
lib/meta.js \
lib/scriptOnly.js \
@ -115,9 +115,6 @@ dist/system-register-only.src.js: lib/*.js $(ESML)/*.js
$(ESML)/loader.js \
$(ESML)/dynamic-only.js \
$(ESML)/system.js \
lib/core.js \
lib/scriptLoader.js \
lib/scriptOnly.js \
lib/register.js \
lib/createSystem.js \
$(ESML)/wrapper-end.js \

View File

@ -1,63 +0,0 @@
/*
* Config
*/
(function() {
/*
Extend config merging one deep only
loader.config({
some: 'random',
config: 'here',
deep: {
config: { too: 'too' }
}
});
<=>
loader.some = 'random';
loader.config = 'here'
loader.deep = loader.deep || {};
loader.deep.config = { too: 'too' };
*/
SystemLoader.prototype.config = function(cfg) {
for (var c in cfg) {
var v = cfg[c];
if (typeof v == 'object' && !(v instanceof Array)) {
this[c] = this[c] || {};
for (var p in v)
this[c][p] = v[p];
}
else
this[c] = v;
}
};
var baseURL;
hookConstructor(function(constructor) {
return function() {
var loader = this;
constructor.call(loader);
baseURL = loader.baseURL;
// support the empty module, as a concept
loader.set('@empty', loader.newModule({}));
};
});
// allow baseURL to be a relative URL
var normalizedBaseURL;
hook('locate', function(locate) {
return function(load) {
if (this.baseURL != normalizedBaseURL) {
normalizedBaseURL = new URL(this.baseURL, baseURL).href;
if (normalizedBaseURL.substr(normalizedBaseURL.length - 1, 1) != '/')
normalizedBaseURL += '/';
this.baseURL = normalizedBaseURL;
}
return Promise.resolve(locate.call(this, load));
};
});
})();

View File

@ -26,26 +26,4 @@ function dedupe(deps) {
if (indexOf.call(newDeps, deps[i]) == -1)
newDeps.push(deps[i])
return newDeps;
}
/*
__useDefault
When a module object looks like:
newModule(
__useDefault: true,
default: 'some-module'
})
Then importing that module provides the 'some-module'
result directly instead of the full module.
Useful for eg module.exports = function() {}
*/
hook('import', function(systemImport) {
return function(name, parentName) {
return systemImport.call(this, name, parentName).then(function(module) {
return module.__useDefault ? module['default'] : module;
});
};
});
}

View File

@ -19,29 +19,33 @@ hookConstructor(function(constructor) {
};
});
var baseURLCache = {};
hook('normalize', function(normalize) {
return function(name, parentName, parentAddress) {
var loader = this;
return Promise.resolve(normalize.call(loader, name, parentName, parentAddress))
.then(function(name) {
var bestMatch, bestMatchLength = 0;
if (name.substr(0, 1) != '.' && name.substr(0, 1) != '/' && !name.match(absURLRegEx)) {
var bestMatch, bestMatchLength = 0;
// now do the global map
for (var p in loader.map) {
if (typeof loader.map[p] != 'string')
throw new TypeError('Map configuration no longer permits object submaps. Use package map instead (`System.packages[name].map`).');
// now do the global map
for (var p in loader.map) {
if (typeof loader.map[p] != 'string')
throw new TypeError('Map configuration no longer permits object submaps. Use package map instead (`System.packages[name].map`).');
if (name.substr(0, p.length) == p && (name.length == p.length || name[p.length] == '/')) {
var curMatchLength = p.split('/').length;
if (curMatchLength <= bestMatchLength)
continue;
bestMatch = p;
bestMatchLength = curMatchLength;
if (name.substr(0, p.length) == p && (name.length == p.length || name[p.length] == '/')) {
var curMatchLength = p.split('/').length;
if (curMatchLength <= bestMatchLength)
continue;
bestMatch = p;
bestMatchLength = curMatchLength;
}
}
}
if (bestMatch)
return loader.map[bestMatch] + name.substr(bestMatch.length);
if (bestMatch)
return loader.map[bestMatch] + name.substr(bestMatch.length);
}
return name;
});

183
lib/misc.js Normal file
View File

@ -0,0 +1,183 @@
var absURLRegEx = /^[^\/]+:\/\//;
(function() {
var baseURI;
hookConstructor(function(constructor) {
return function() {
constructor.call(this);
// by default ModuleLoader sets this.baseURL to baseURI
baseURI = this.baseURL;
// support the empty module, as a concept
this.set('@empty', this.newModule({}));
};
});
// caches baseURL URL object
// also allows baseURL to be relative to the baseURI
function getAbsBaseURL(baseURL) {
return baseURLCache[baseURL] = baseURLCache[baseURL] || new URL(baseURL + (baseURL[baseURL.length - 1] != '/' ? '/' : ''), baseURI);
}
/*
Normalization
Dot-Normalization is applied for relative module names, when the parent name
is not a URL itself.
../, ./ are resolved relative to plain path parentNames
where "plain paths" are defined as not beginning with protocol://, /, ./
Inner ../ or ./ are not supported
Names that dot below the parent name skip to use URL normalization
The goal is for normalize to normalize all names into a space containing a union
of "plain paths", and absolute URLs.
Plain paths will then either be packages or paths in locate
or they will be baseURL-relative in locate
Absolute paths corresponding to paths within baseURL are converted
back into plain paths to ensure a unique representation in this space.
In SystemJS we assume and define normalize to never output a module
name not an absolute URL or plain path.
*/
hook('normalize', function() {
return function(name, parentName, parentAddress) {
// percent encode just '#' in module names
// according to https://github.com/jorendorff/js-loaders/blob/master/browser-loader.js#L238
// we should encode everything, but it breaks for servers that don't expect it
// like in (https://github.com/systemjs/systemjs/issues/168)
if (isBrowser)
name = name.replace(/#/g, '%23');
// Relative Normalization
if (name[0] == '.' && parentName) {
// if parent is a URL, apply URL normalization
if (parentName.match(absURLRegEx))
return new URL(name, parentName).href;
// Dot Normalization
// skip leading ./
var parts = name.split('/');
var i = 0;
var dotdots = 0;
while (parts[i] == '.') {
i++;
if (i == parts.length)
throw new TypeError('Invalid module name');
}
// count dot dots
while (parts[i] == '..') {
i++;
dotdots++;
if (i == parts.length)
throw new TypeError('Invalid module name');
}
var parentParts = parentName.split('/');
parts = parts.splice(i, parts.length - i);
// if backtracking below the parent name, just set to the base-level like URLs
// NB if parentAddress is supported in the spec, we can URL normalize against it here instead
if (dotdots <= parentParts.length)
parts = parentParts.splice(0, parentParts.length - dotdots - 1).concat(parts);
name = parts.join('/');
}
// if url-relative, normalize to baseURL
// if within baseURL, convert into corresponding plain path
if (name[0] == '.' || name[0] == '/') {
var baseURL = getAbsBaseURL(this.baseURL);
var normalizedURL = new URL(name, baseURL);
if (normalizedURL.origin == baseURL.origin && normalizedURL.pathname.substr(0, baseURL.pathname.length) == baseURL.pathname)
name = normalizedURL.pathname.substr(baseURL.pathname.length);
else
name = normalizedURL.href;
}
return name;
};
});
/*
Locate
Any plain names from normalize are normalized into absolute URLs
First they are checked against paths
Paths is moved from normalize in the module spec to locate in the System spec
This isn't inconsistent because the module spec normalizes everything to absolute URLs,
while the System spec defines its own intermediate union of URLs and plain paths
*/
hook('locate', function() {
return function(load) {
if (load.name.match(absURLRegEx))
return load.name;
name = applyPaths(this, load.name);
return new URL(name, getAbsBaseURL(this.baseURL)).href;
};
});
/*
__useDefault
When a module object looks like:
newModule(
__useDefault: true,
default: 'some-module'
})
Then importing that module provides the 'some-module'
result directly instead of the full module.
Useful for eg module.exports = function() {}
*/
hook('import', function(systemImport) {
return function(name, parentName, parentAddress) {
return systemImport.call(this, name, parentName, parentAddress).then(function(module) {
return module.__useDefault ? module['default'] : module;
});
};
});
/*
Extend config merging one deep only
loader.config({
some: 'random',
config: 'here',
deep: {
config: { too: 'too' }
}
});
<=>
loader.some = 'random';
loader.config = 'here'
loader.deep = loader.deep || {};
loader.deep.config = { too: 'too' };
*/
SystemLoader.prototype.config = function(cfg) {
for (var c in cfg) {
var v = cfg[c];
if (typeof v == 'object' && !(v instanceof Array)) {
this[c] = this[c] || {};
for (var p in v)
this[c][p] = v[p];
}
else
this[c] = v;
}
};
})();