/** * Load es modules in browser. * rollup.browser.js is required. * * [Usage]: * * // AMD config like. * requireES.config({ * baseUrl: '..', * paths: { * ... * }, * packages: [ * {...}, ... * ], * urlArgs: +new Date(), * sourceMap: true // Enable sourceMap for debugging. `false` by default. * }); * * requireES([ * 'xxx/moduleA', * 'yyy/moduleB' * ], function (moduleA, moduleB) { * ... * }); * * [Caution]: * * 1) Modules are not shared between different calling of `requireES(...)`. * * 2) Whether import `*` or `default` is determined by the module itself. * That is, if the module (like `xxx/SomeClz`) only export `default` , it * imports `default`, otherwise (like `xxx/util`) it imports `*`. */ /* global define, ActiveXObject */ (function (global, factory) { typeof define === 'function' && define.amd ? define(['rollup'], factory) : (global.requireES = factory(global.rollup)); }(this, function (rollup) { 'use strict'; var TOP_MODULE_NAME = 'topModuleInRequireES'; var amdCfg = { baseUrl: cwd(), paths: {}, packages: [], urlArgs: null }; /** * Like AMD config. * * @param {Object} cfg { * @param {string} [cfg.baseUrl='.'] * @param {Object} [cfg.paths={}] * @param {Array.} [cfg.packages=[]] * @param {string} [cfg.urlArgs=''] * @param {boolean} [cfg.sourceMap=false] */ function amdConfig(cfg) { if (cfg.baseUrl != null) { amdCfg.baseUrl = resolve(cwd(), cfg.baseUrl); } if (cfg.paths) { amdCfg.paths = extend({}, cfg.paths); } if (cfg.packages) { amdCfg.packages.length = 0; for (var i = 0; i < cfg.packages.length; i++) { amdCfg.packages[i] = extend({}, cfg.packages[i]); } } if (cfg.urlArgs != null) { amdCfg.urlArgs = cfg.urlArgs; } if (cfg.sourceMap != null) { amdCfg.sourceMap = cfg.sourceMap; } } /** * Load es modules and convert to AMD modules. * * @param {Array.} moduleIds like ['./echarts', ...] * @param {Function} onload Arguments: loaded modules, * which has been converted to AMD module. */ function requireES(moduleIds, onload) { if (!(moduleIds instanceof Array)) { throw new Error('`path` should be an array'); } if (!moduleIds.length) { return; } var topCode = generateTopModuleCode(moduleIds); rollup.rollup({ input: TOP_MODULE_NAME, legacy: true, plugins: [{ resolveId: function (importee, importor) { if (importee === TOP_MODULE_NAME) { return importee; } // console.log('resolveid', importee, importor); return getAbsolutePath( importee, importor !== TOP_MODULE_NAME ? importor : null ); }, load: function (path) { if (path === TOP_MODULE_NAME) { return topCode; } // TODO Use tag to void browser cache and cache manually. return ajax(location.origin + path); } }] }).then(function (bundle) { return bundle.generate({ format: 'iife', legacy: true, // But only bundle.write support generating inline source map. sourcemap: 'inline', name: TOP_MODULE_NAME }); }).then(function (result) { var code = result.code; if (amdCfg.sourceMap) { code = addSourceMap(code, result.map); } var modules = (new Function( 'var __DEV__ = true; ' + code + '\n return ' + TOP_MODULE_NAME ))(); var exportsList = []; for (var i = 0; i < moduleIds.length; i++) { var mod = modules['m' + i]; // Guess whether `*` or `default` is required: if only `default` // exported, like 'xxx/SomeClz', `default` is required. if (onlyDefaultExported(mod)) { mod = mod['default']; } exportsList.push(mod); } onload && onload.apply(null, exportsList); }); } requireES.config = amdConfig; function onlyDefaultExported(mod) { for (var name in mod) { if (mod.hasOwnProperty(name)) { if (name !== 'default') { return false; } } } return true; } function generateTopModuleCode(moduleIds) { var code = []; for (var i = 0; i < moduleIds.length; i++) { var moduleId = moduleIds[i]; code.push('import * as m' + i + ' from "' + moduleId + '";'); } for (var i = 0; i < moduleIds.length; i++) { code.push('export {m' + i + '};'); } return code.join('\n'); } // Get absolute path. `basePath` can be omitted if moduleId is absolute. function getAbsolutePath(moduleId, basePath) { moduleId = addExt(moduleId); for (var path in amdCfg.paths) { if (amdCfg.paths.hasOwnProperty(path)) { if (moduleId.indexOf(path) === 0) { moduleId = moduleId.replace(path, amdCfg.paths[path]); return resolve(amdCfg.baseUrl, moduleId); } } } for (var i = 0; i < amdCfg.packages.length; i++) { var packageCfg = amdCfg.packages[i]; var moduleIdArr = moduleId.split('/'); if (moduleIdArr[0] === packageCfg.name) { moduleIdArr[0] = packageCfg.location; if (!moduleIdArr[1]) { moduleIdArr[1] = packageCfg.main; } moduleId = moduleIdArr.join('/'); return resolve(amdCfg.baseUrl, moduleId); } } if (basePath) { moduleId = resolve(dir(basePath), moduleId); } if (moduleId.charAt(0) !== '/') { throw new Error('"' + moduleId + '" can not be found.'); } return moduleId; } function addExt(moduleId) { if (moduleId.split('/').pop().indexOf('.') < 0) { moduleId += '.js'; } return moduleId; } function ajax(toUrl) { if (amdCfg.urlArgs != null) { toUrl += '?' + amdCfg.urlArgs; } return new Promise(function (promiseResolve, promiseReject) { var xhr = window.XMLHttpRequest ? new XMLHttpRequest() : new ActiveXObject('Microsoft.XMLHTTP'); xhr.open('GET', toUrl, true); xhr.onreadystatechange = function () { if (xhr.readyState === 4) { (xhr.status >= 200 && xhr.status < 300) ? promiseResolve(xhr.responseText) : promiseReject({ status: xhr.status, content: xhr.responseText }); xhr.onreadystatechange = new Function(); xhr = null; } }; xhr.send(null); }); } // Nodejs `path.resolve`. function resolve() { var resolvedPath = ''; var resolvedAbsolute; for (var i = arguments.length - 1; i >= 0 && !resolvedAbsolute; i--) { var path = arguments[i]; if (path) { resolvedPath = path + '/' + resolvedPath; resolvedAbsolute = path[0] === '/'; } } if (!resolvedAbsolute) { throw new Error('At least one absolute path should be input.'); } // Normalize the path resolvedPath = normalizePathArray(resolvedPath.split('/'), false).join('/'); return '/' + resolvedPath; } // resolves . and .. elements in a path array with directory names there // must be no slashes or device names (c:\) in the array // (so also no leading and trailing slashes - it does not distinguish // relative and absolute paths) function normalizePathArray(parts, allowAboveRoot) { var res = []; for (var i = 0; i < parts.length; i++) { var p = parts[i]; // ignore empty parts if (!p || p === '.') { continue; } if (p === '..') { if (res.length && res[res.length - 1] !== '..') { res.pop(); } else if (allowAboveRoot) { res.push('..'); } } else { res.push(p); } } return res; } function addSourceMap(code, map) { // Use unescape(encodeURIComponent) to avoid the error on Chrome: // Uncaught (in promise) DOMException: Failed to execute 'btoa' on 'Window': // The string to be encoded contains characters outside of the Latin1 range var dataURI = btoa(unescape(encodeURIComponent(map.toString()))); // jshint ignore:line dataURI = 'data:application/json;charset=utf-8;base64,' + dataURI; // Split the string to prevent sourcemap tooling from mistaking // this for an actual sourceMappingURL. code += '//# ' + 'sourceMa' + 'ppingURL' + '=' + dataURI + '\n'; return code; } function cwd() { // Only support that works in browser. return dir(location.pathname); } function dir(path) { if (path) { return path.charAt(path.length - 1) === '/' ? path : resolve(path, '..'); } } function extend(target, source) { for (var key in source) { if (source.hasOwnProperty(key)) { target[key] = source[key]; } } return target; } return requireES; }));