blob: 4d959edd281e0402df0602ab0c9fdc6fc2efbe66 [file] [log] [blame]
/*
* 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;
}));