systemjs/src/format-helpers.js
2017-05-26 15:26:55 +02:00

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);
}