mirror of
https://github.com/systemjs/systemjs.git
synced 2026-01-18 14:53:14 +00:00
262 lines
8.4 KiB
JavaScript
262 lines
8.4 KiB
JavaScript
/*
|
|
* Package Configuration Extension
|
|
*
|
|
* Example:
|
|
*
|
|
* System.packages = {
|
|
* jquery: {
|
|
* main: 'index.js', // when not set, package name is requested directly
|
|
* format: 'amd',
|
|
* defaultExtension: 'js',
|
|
* meta: {
|
|
* '*.ts': {
|
|
* loader: 'typescript'
|
|
* },
|
|
* 'vendor/sizzle.js': {
|
|
* format: 'global'
|
|
* }
|
|
* },
|
|
* map: {
|
|
* // map internal require('sizzle') to local require('./vendor/sizzle')
|
|
* sizzle: './vendor/sizzle.js',
|
|
* // map any internal or external require of 'jquery/vendor/another' to 'another/index.js'
|
|
* './vendor/another.js': './another/index.js',
|
|
* // test.js / test -> lib/test.js
|
|
* './test.js': './lib/test.js',
|
|
* },
|
|
* env: {
|
|
* 'browser': {
|
|
* main: 'browser.js'
|
|
* }
|
|
* }
|
|
* }
|
|
* };
|
|
*
|
|
* Then:
|
|
* import 'jquery' -> jquery/index.js
|
|
* import 'jquery/submodule' -> jquery/submodule.js
|
|
* import 'jquery/submodule.ts' -> jquery/submodule.ts loaded as typescript
|
|
* import 'jquery/vendor/another' -> another/index.js
|
|
*
|
|
* Detailed Behaviours
|
|
* - main is the only property where a leading "./" can be added optionally
|
|
* - map and defaultExtension are applied to the main
|
|
* - defaultExtension adds the extension only if no other extension is present
|
|
* - defaultJSExtensions applies after map when defaultExtension is not set
|
|
* - if a meta value is available for a module, map and defaultExtension are skipped
|
|
* - like global map, package map also applies to subpaths (sizzle/x, ./vendor/another/sub)
|
|
*
|
|
* In addition, the following meta properties will be allowed to be package
|
|
* -relative as well in the package meta config:
|
|
*
|
|
* - loader
|
|
* - alias
|
|
*
|
|
*/
|
|
(function() {
|
|
|
|
hookConstructor(function(constructor) {
|
|
return function() {
|
|
constructor.call(this);
|
|
this.packages = {};
|
|
};
|
|
});
|
|
|
|
function getPackage(name) {
|
|
for (var p in this.packages) {
|
|
if (name.substr(0, p.length) === p && (name.length === p.length || name[p.length] === '/'))
|
|
return p;
|
|
}
|
|
}
|
|
|
|
function getPackageConfig(loader, pkgName) {
|
|
var pkgConfig = loader.packages[pkgName];
|
|
|
|
if (!pkgConfig.env)
|
|
return Promise.resolve(pkgConfig);
|
|
|
|
// check environment conditions
|
|
// default environment condition is '@env' in package or '@system-env' globally
|
|
return loader['import'](pkgConfig.map['@env'] || '@system-env', pkgName)
|
|
.then(function(env) {
|
|
// derived config object
|
|
var pkg = {};
|
|
for (var p in pkgConfig)
|
|
if (p !== 'map' & p !== 'env')
|
|
pkg[p] = pkgConfig[p];
|
|
|
|
pkg.map = {};
|
|
for (var p in pkgConfig.map)
|
|
pkg.map[p] = pkgConfig.map[p];
|
|
|
|
for (var e in pkgConfig.env) {
|
|
if (env[e]) {
|
|
var envConfig = pkgConfig.env[e];
|
|
if (envConfig.main)
|
|
pkg.main = envConfig.main;
|
|
for (var m in envConfig.map)
|
|
pkg.map[m] = envConfig.map[m];
|
|
}
|
|
}
|
|
|
|
// store the derived environment config so we have this cached for next time
|
|
loader.packages[pkgName] = pkg;
|
|
|
|
return pkg;
|
|
});
|
|
}
|
|
|
|
function applyMap(map, name) {
|
|
var bestMatch, bestMatchLength = 0;
|
|
|
|
for (var p in 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 (bestMatch)
|
|
return map[bestMatch] + name.substr(bestMatch.length);
|
|
}
|
|
|
|
SystemJSLoader.prototype.normalizeSync = SystemJSLoader.prototype.normalize;
|
|
|
|
hook('normalize', function(normalize) {
|
|
return function(name, parentName) {
|
|
// apply contextual package map first
|
|
if (parentName) {
|
|
var parentPackage = getPackage.call(this, parentName) ||
|
|
this.defaultJSExtensions && parentName.substr(parentName.length - 3, 3) == '.js' &&
|
|
getPackage.call(this, parentName.substr(0, parentName.length - 3));
|
|
}
|
|
|
|
if (parentPackage && name[0] !== '.') {
|
|
var parentMap = this.packages[parentPackage].map;
|
|
if (parentMap) {
|
|
name = applyMap(parentMap, name) || name;
|
|
|
|
// relative maps are package-relative
|
|
if (name[0] === '.')
|
|
parentName = parentPackage + '/';
|
|
}
|
|
}
|
|
|
|
var defaultJSExtension = this.defaultJSExtensions && name.substr(name.length - 3, 3) != '.js';
|
|
|
|
// apply global map, relative normalization
|
|
var normalized = normalize.call(this, name, parentName);
|
|
|
|
// undo defaultJSExtension
|
|
if (normalized.substr(normalized.length - 3, 3) != '.js')
|
|
defaultJSExtension = false;
|
|
if (defaultJSExtension)
|
|
normalized = normalized.substr(0, normalized.length - 3);
|
|
|
|
// check if we are inside a package
|
|
var pkgName = getPackage.call(this, normalized);
|
|
|
|
if (pkgName) {
|
|
return getPackageConfig(this, pkgName)
|
|
.then(function(pkg) {
|
|
// main
|
|
if (pkgName === normalized && pkg.main)
|
|
normalized += '/' + (pkg.main.substr(0, 2) == './' ? pkg.main.substr(2) : pkg.main);
|
|
|
|
if (normalized.substr(pkgName.length) == '/')
|
|
return normalized;
|
|
|
|
// defaultExtension & defaultJSExtension
|
|
// if we have meta for this package, don't do defaultExtensions
|
|
var defaultExtension = '';
|
|
if (!pkg.meta || !pkg.meta[normalized.substr(pkgName.length + 1)]) {
|
|
// apply defaultExtension
|
|
|
|
if ('defaultExtension' in pkg) {
|
|
if (pkg.defaultExtension !== false && normalized.split('/').pop().indexOf('.') == -1)
|
|
defaultExtension = '.' + pkg.defaultExtension;
|
|
}
|
|
// apply defaultJSExtensions if defaultExtension not set
|
|
else if (defaultJSExtension) {
|
|
defaultExtension = '.js';
|
|
}
|
|
}
|
|
|
|
// apply submap checking without then with defaultExtension
|
|
var subPath = '.' + normalized.substr(pkgName.length);
|
|
var mapped = applyMap(pkg.map, subPath) || defaultExtension && applyMap(pkg.map, subPath + defaultExtension);
|
|
if (mapped)
|
|
normalized = mapped.substr(0, 2) == './' ? pkgName + mapped.substr(1) : mapped;
|
|
else
|
|
normalized += defaultExtension;
|
|
|
|
|
|
return normalized;
|
|
});
|
|
}
|
|
|
|
// add back defaultJSExtension if not a package
|
|
if (defaultJSExtension)
|
|
normalized += '.js';
|
|
|
|
return normalized;
|
|
};
|
|
});
|
|
|
|
hook('locate', function(locate) {
|
|
return function(load) {
|
|
var loader = this;
|
|
return Promise.resolve(locate.call(this, load))
|
|
.then(function(address) {
|
|
var pkgName = getPackage.call(loader, load.name);
|
|
if (pkgName) {
|
|
var pkg = loader.packages[pkgName];
|
|
|
|
// format
|
|
if (pkg.format)
|
|
load.metadata.format = load.metadata.format || pkg.format;
|
|
|
|
// loader
|
|
if (pkg.loader)
|
|
load.metadata.loader = load.metadata.loader || pkg.loader;
|
|
|
|
if (pkg.meta) {
|
|
// wildcard meta
|
|
var meta = {};
|
|
var bestDepth = 0;
|
|
var wildcardIndex;
|
|
for (var module in pkg.meta) {
|
|
wildcardIndex = module.indexOf('*');
|
|
if (wildcardIndex === -1)
|
|
continue;
|
|
if (module.substr(0, wildcardIndex) === load.name.substr(0, wildcardIndex)
|
|
&& module.substr(wildcardIndex + 1) === load.name.substr(load.name.length - module.length + wildcardIndex + 1)) {
|
|
var depth = module.split('/').length;
|
|
if (depth > bestDepth)
|
|
bestDetph = depth;
|
|
extend(meta, pkg.meta[module], bestDepth != depth);
|
|
}
|
|
}
|
|
// exact meta
|
|
var exactMeta = pkg.meta[load.name.substr(pkgName.length + 1)];
|
|
if (exactMeta)
|
|
extend(meta, exactMeta);
|
|
|
|
// allow alias and loader to be package-relative
|
|
if (meta.alias && meta.alias.substr(0, 2) == './')
|
|
meta.alias = pkgName + meta.alias.substr(1);
|
|
if (meta.loader && meta.loader.substr(0, 2) == './')
|
|
meta.loader = pkgName + meta.loader.substr(1);
|
|
|
|
extend(load.metadata, meta);
|
|
}
|
|
}
|
|
|
|
return address;
|
|
});
|
|
};
|
|
});
|
|
|
|
})(); |