|
/*
|
* Licensed to the Apache Software Foundation (ASF) under one
|
* or more contributor license agreements. See the NOTICE file
|
* distributed with this work for additional information
|
* regarding copyright ownership. The ASF licenses this file
|
* to you under the Apache License, Version 2.0 (the
|
* "License"); you may not use this file except in compliance
|
* with the License. You may obtain a copy of the License at
|
*
|
* http://www.apache.org/licenses/LICENSE-2.0
|
*
|
* Unless required by applicable law or agreed to in writing,
|
* software distributed under the License is distributed on an
|
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
* KIND, either express or implied. See the License for the
|
* specific language governing permissions and limitations
|
* under the License.
|
*/
|
|
/**
|
* 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.<Object>} [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.<string>} 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;
|
|
}));
|