mirror of
https://github.com/systemjs/systemjs.git
synced 2026-01-18 14:53:14 +00:00
415 lines
12 KiB
JavaScript
415 lines
12 KiB
JavaScript
|
|
import { isWindows, global, readMemberExpression, cjsRequireRegEx, noop } from './common.js';
|
|
|
|
export default function (loader) {
|
|
loader.set('@@cjs-helpers', loader.newModule({
|
|
requireResolve: requireResolve.bind(loader),
|
|
getPathVars: getPathVars
|
|
}));
|
|
|
|
loader.set('@@global-helpers', loader.newModule({
|
|
prepareGlobal: prepareGlobal
|
|
}));
|
|
|
|
/*
|
|
AMD-compatible require
|
|
To copy RequireJS, set window.require = window.requirejs = loader.amdRequire
|
|
*/
|
|
function require (names, callback, errback, referer) {
|
|
// in amd, first arg can be a config object... we just ignore
|
|
if (typeof names === 'object' && !(names instanceof Array))
|
|
return require.apply(null, Array.prototype.splice.call(arguments, 1, arguments.length - 1));
|
|
|
|
// amd require
|
|
if (typeof names === 'string' && typeof callback === 'function')
|
|
names = [names];
|
|
if (names instanceof Array) {
|
|
var dynamicRequires = [];
|
|
for (var i = 0; i < names.length; i++)
|
|
dynamicRequires.push(loader.import(names[i], referer));
|
|
Promise.all(dynamicRequires).then(function (modules) {
|
|
for (var i = 0; i < modules.length; i++)
|
|
modules[i] = modules[i].__useDefault || modules[i];
|
|
if (callback)
|
|
callback.apply(null, modules);
|
|
}, errback);
|
|
}
|
|
|
|
// commonjs require
|
|
else if (typeof names === 'string') {
|
|
var normalized = loader.decanonicalize(names, referer);
|
|
var module = loader.get(normalized);
|
|
if (!module)
|
|
throw new Error('Module not already loaded loading "' + names + '" as ' + normalized + (referer ? ' from "' + referer + '".' : '.'));
|
|
return module.__useDefault || module;
|
|
}
|
|
|
|
else
|
|
throw new TypeError('Invalid require');
|
|
}
|
|
|
|
function define (name, deps, factory) {
|
|
if (typeof name !== 'string') {
|
|
factory = deps;
|
|
deps = name;
|
|
name = null;
|
|
}
|
|
|
|
if (!(deps instanceof Array)) {
|
|
factory = deps;
|
|
deps = ['require', 'exports', 'module'].splice(0, factory.length);
|
|
}
|
|
|
|
if (typeof factory !== 'function')
|
|
factory = (function (factory) {
|
|
return function() { return factory; }
|
|
})(factory);
|
|
|
|
if (!name) {
|
|
if (curMetaDeps) {
|
|
deps = deps.concat(curMetaDeps);
|
|
curMetaDeps = undefined;
|
|
}
|
|
}
|
|
|
|
// remove system dependencies
|
|
var requireIndex, exportsIndex, moduleIndex;
|
|
|
|
if ((requireIndex = deps.indexOf('require')) !== -1) {
|
|
|
|
deps.splice(requireIndex, 1);
|
|
|
|
// only trace cjs requires for non-named
|
|
// named defines assume the trace has already been done
|
|
if (!name)
|
|
deps = deps.concat(amdGetCJSDeps(factory.toString(), requireIndex));
|
|
}
|
|
|
|
if ((exportsIndex = deps.indexOf('exports')) !== -1)
|
|
deps.splice(exportsIndex, 1);
|
|
|
|
if ((moduleIndex = deps.indexOf('module')) !== -1)
|
|
deps.splice(moduleIndex, 1);
|
|
|
|
function execute (req, exports, module) {
|
|
var depValues = [];
|
|
for (var i = 0; i < deps.length; i++)
|
|
depValues.push(req(deps[i]));
|
|
|
|
module.uri = module.id;
|
|
|
|
module.config = noop;
|
|
|
|
// add back in system dependencies
|
|
if (moduleIndex !== -1)
|
|
depValues.splice(moduleIndex, 0, module);
|
|
|
|
if (exportsIndex !== -1)
|
|
depValues.splice(exportsIndex, 0, exports);
|
|
|
|
if (requireIndex !== -1) {
|
|
var contextualRequire = function (names, callback, errback) {
|
|
if (typeof names === 'string' && typeof callback !== 'function')
|
|
return req(names);
|
|
return require.call(loader, names, callback, errback, module.id);
|
|
};
|
|
contextualRequire.toUrl = function (name) {
|
|
return loader.normalizeSync(name, module.id);
|
|
};
|
|
depValues.splice(requireIndex, 0, contextualRequire);
|
|
}
|
|
|
|
// set global require to AMD require
|
|
var curRequire = global.require;
|
|
global.require = require;
|
|
|
|
var output = factory.apply(exportsIndex === -1 ? global : exports, depValues);
|
|
|
|
global.require = curRequire;
|
|
|
|
if (typeof output !== 'undefined')
|
|
module.exports = output;
|
|
}
|
|
|
|
// anonymous define
|
|
if (!name) {
|
|
loader.registerDynamic(deps, false, curEsModule ? wrapEsModuleExecute(execute) : execute);
|
|
}
|
|
else {
|
|
loader.registerDynamic(name, deps, false, execute);
|
|
|
|
// if we don't have any other defines,
|
|
// then let this be an anonymous define
|
|
// this is just to support single modules of the form:
|
|
// define('jquery')
|
|
// still loading anonymously
|
|
// because it is done widely enough to be useful
|
|
// as soon as there is more than one define, this gets removed though
|
|
if (lastNamedDefine) {
|
|
lastNamedDefine = undefined;
|
|
multipleNamedDefines = true;
|
|
}
|
|
else if (!multipleNamedDefines) {
|
|
lastNamedDefine = [deps, execute];
|
|
}
|
|
}
|
|
}
|
|
define.amd = {};
|
|
|
|
loader.amdDefine = define;
|
|
loader.amdRequire = require;
|
|
}
|
|
|
|
// CJS
|
|
var windowOrigin;
|
|
if (typeof window !== 'undefined' && typeof document !== 'undefined' && window.location)
|
|
windowOrigin = location.protocol + '//' + location.hostname + (location.port ? ':' + location.port : '');
|
|
|
|
function stripOrigin(path) {
|
|
if (path.substr(0, 8) === 'file:///')
|
|
return path.substr(7 + !!isWindows);
|
|
|
|
if (windowOrigin && path.substr(0, windowOrigin.length) === windowOrigin)
|
|
return path.substr(windowOrigin.length);
|
|
|
|
return path;
|
|
}
|
|
|
|
export function requireResolve (request, parentId) {
|
|
return stripOrigin(this.normalizeSync(request, parentId));
|
|
}
|
|
|
|
export function getPathVars (moduleId) {
|
|
// remove any plugin syntax
|
|
var pluginIndex = moduleId.lastIndexOf('!');
|
|
var filename;
|
|
if (pluginIndex !== -1)
|
|
filename = moduleId.substr(0, pluginIndex);
|
|
else
|
|
filename = moduleId;
|
|
|
|
var dirname = filename.split('/');
|
|
dirname.pop();
|
|
dirname = dirname.join('/');
|
|
|
|
return {
|
|
filename: stripOrigin(filename),
|
|
dirname: stripOrigin(dirname)
|
|
};
|
|
}
|
|
|
|
var commentRegEx = /(^|[^\\])(\/\*([\s\S]*?)\*\/|([^:]|^)\/\/(.*)$)/mg;
|
|
var stringRegEx = /("[^"\\\n\r]*(\\.[^"\\\n\r]*)*"|'[^'\\\n\r]*(\\.[^'\\\n\r]*)*')/g;
|
|
|
|
// used to support leading #!/usr/bin/env in scripts as supported in Node
|
|
var hashBangRegEx = /^\#\!.*/;
|
|
|
|
// extract CJS dependencies from source text via regex static analysis
|
|
// read require('x') statements not in comments or strings
|
|
export function getCJSDeps (source) {
|
|
cjsRequireRegEx.lastIndex = commentRegEx.lastIndex = stringRegEx.lastIndex = 0;
|
|
|
|
var deps = [];
|
|
|
|
var match;
|
|
|
|
// track string and comment locations for unminified source
|
|
var stringLocations = [], commentLocations = [];
|
|
|
|
function inLocation (locations, match) {
|
|
for (var i = 0; i < locations.length; i++)
|
|
if (locations[i][0] < match.index && locations[i][1] > match.index)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
if (source.length / source.split('\n').length < 200) {
|
|
while (match = stringRegEx.exec(source))
|
|
stringLocations.push([match.index, match.index + match[0].length]);
|
|
|
|
// TODO: track template literals here before comments
|
|
|
|
while (match = commentRegEx.exec(source)) {
|
|
// only track comments not starting in strings
|
|
if (!inLocation(stringLocations, match))
|
|
commentLocations.push([match.index + match[1].length, match.index + match[0].length - 1]);
|
|
}
|
|
}
|
|
|
|
while (match = cjsRequireRegEx.exec(source)) {
|
|
// ensure we're not within a string or comment location
|
|
if (!inLocation(stringLocations, match) && !inLocation(commentLocations, match)) {
|
|
var dep = match[1].substr(1, match[1].length - 2);
|
|
// skip cases like require('" + file + "')
|
|
if (dep.match(/"|'/))
|
|
continue;
|
|
deps.push(dep);
|
|
}
|
|
}
|
|
|
|
return deps;
|
|
}
|
|
|
|
// Global
|
|
// bare minimum ignores
|
|
var ignoredGlobalProps = ['_g', 'sessionStorage', 'localStorage', 'clipboardData', 'frames', 'frameElement', 'external',
|
|
'mozAnimationStartTime', 'webkitStorageInfo', 'webkitIndexedDB', 'mozInnerScreenY', 'mozInnerScreenX'];
|
|
|
|
var globalSnapshot;
|
|
function globalIterator (globalName) {
|
|
if (ignoredGlobalProps.indexOf(globalName) !== -1)
|
|
return;
|
|
try {
|
|
var value = global[globalName];
|
|
}
|
|
catch (e) {
|
|
ignoredGlobalProps.push(globalName);
|
|
}
|
|
this(globalName, value);
|
|
}
|
|
|
|
export function getGlobalValue (exports) {
|
|
if (typeof exports === 'string')
|
|
return readMemberExpression(exports, global);
|
|
|
|
if (!(exports instanceof Array))
|
|
throw new Error('Global exports must be a string or array.');
|
|
|
|
var globalValue = {};
|
|
for (var i = 0; i < exports.length; i++)
|
|
globalValue[exports[i].split('.').pop()] = readMemberExpression(exports[i], global);
|
|
return globalValue;
|
|
}
|
|
|
|
export function prepareGlobal (moduleName, exports, globals, encapsulate) {
|
|
// disable module detection
|
|
var curDefine = global.define;
|
|
|
|
global.define = undefined;
|
|
|
|
// set globals
|
|
var oldGlobals;
|
|
if (globals) {
|
|
oldGlobals = {};
|
|
for (var g in globals) {
|
|
oldGlobals[g] = global[g];
|
|
global[g] = globals[g];
|
|
}
|
|
}
|
|
|
|
// store a complete copy of the global object in order to detect changes
|
|
if (!exports) {
|
|
globalSnapshot = {};
|
|
|
|
Object.keys(global).forEach(globalIterator, function (name, value) {
|
|
globalSnapshot[name] = value;
|
|
});
|
|
}
|
|
|
|
// return function to retrieve global
|
|
return function () {
|
|
var globalValue = exports ? getGlobalValue(exports) : {};
|
|
|
|
var singleGlobal;
|
|
var multipleExports = !!exports;
|
|
|
|
if (!exports || encapsulate)
|
|
Object.keys(global).forEach(globalIterator, function (name, value) {
|
|
if (globalSnapshot[name] === value)
|
|
return;
|
|
if (value === undefined)
|
|
return;
|
|
|
|
// allow global encapsulation where globals are removed
|
|
if (encapsulate)
|
|
global[name] = undefined;
|
|
|
|
if (!exports) {
|
|
globalValue[name] = value;
|
|
|
|
if (singleGlobal !== undefined) {
|
|
if (!multipleExports && singleGlobal !== value)
|
|
multipleExports = true;
|
|
}
|
|
else {
|
|
singleGlobal = value;
|
|
}
|
|
}
|
|
});
|
|
|
|
globalValue = multipleExports ? globalValue : singleGlobal;
|
|
|
|
// revert globals
|
|
if (oldGlobals) {
|
|
for (var g in oldGlobals)
|
|
global[g] = oldGlobals[g];
|
|
}
|
|
global.define = curDefine;
|
|
|
|
return globalValue;
|
|
};
|
|
}
|
|
|
|
// AMD
|
|
var cjsRequirePre = "(?:^|[^$_a-zA-Z\\xA0-\\uFFFF.])";
|
|
var cjsRequirePost = "\\s*\\(\\s*(\"([^\"]+)\"|'([^']+)')\\s*\\)";
|
|
var fnBracketRegEx = /\(([^\)]*)\)/;
|
|
var wsRegEx = /^\s+|\s+$/g;
|
|
|
|
var requireRegExs = {};
|
|
|
|
function amdGetCJSDeps(source, requireIndex) {
|
|
|
|
// remove comments
|
|
source = source.replace(commentRegEx, '');
|
|
|
|
// determine the require alias
|
|
var params = source.match(fnBracketRegEx);
|
|
var requireAlias = (params[1].split(',')[requireIndex] || 'require').replace(wsRegEx, '');
|
|
|
|
// find or generate the regex for this requireAlias
|
|
var requireRegEx = requireRegExs[requireAlias] || (requireRegExs[requireAlias] = new RegExp(cjsRequirePre + requireAlias + cjsRequirePost, 'g'));
|
|
|
|
requireRegEx.lastIndex = 0;
|
|
|
|
var deps = [];
|
|
|
|
var match;
|
|
while (match = requireRegEx.exec(source))
|
|
deps.push(match[2] || match[3]);
|
|
|
|
return deps;
|
|
}
|
|
|
|
function wrapEsModuleExecute (execute) {
|
|
return function (require, exports, module) {
|
|
execute(require, exports, module);
|
|
exports = module.exports;
|
|
if ((typeof exports === 'object' || typeof exports === 'function') && !('__esModule' in exports))
|
|
Object.defineProperty(module.exports, '__esModule', {
|
|
value: true
|
|
});
|
|
};
|
|
}
|
|
|
|
// generate anonymous define from singular named define
|
|
var multipleNamedDefines = false;
|
|
var lastNamedDefine;
|
|
var curMetaDeps;
|
|
var curEsModule = false;
|
|
export function clearLastDefine (metaDeps, esModule) {
|
|
curMetaDeps = metaDeps;
|
|
curEsModule = esModule;
|
|
lastNamedDefine = undefined;
|
|
multipleNamedDefines = false;
|
|
}
|
|
export function registerLastDefine (loader) {
|
|
if (lastNamedDefine)
|
|
loader.registerDynamic(curMetaDeps ? lastNamedDefine[0].concat(curMetaDeps) : lastNamedDefine[0],
|
|
false, curEsModule ? wrapEsModuleExecute(lastNamedDefine[1]) : lastNamedDefine[1]);
|
|
|
|
// bundles are an empty module
|
|
else if (multipleNamedDefines)
|
|
loader.registerDynamic([], false, noop);
|
|
}
|