mya-command-release
Version:
mya release command.
196 lines (172 loc) • 5.93 kB
JavaScript
/* runtime plugin for mya */
(function(global) {
function HotModuleReplacementPlugin(options) {
this.require = options.require;
this.define = options.define;
this.factoryMap = options.factoryMap;
this.modulesMap = options.modulesMap;
this.hotUpdateMap = {};
this.selfAcceptedMap = {};
this.data = {};
}
HotModuleReplacementPlugin.prototype = {
apply: function() {
},
accept: function(id, callback) {
if (typeof callback === 'undefined') { // module.hot.accpet(id)
this.selfAcceptedMap[id] = true;
} else {
this.hotUpdateMap[id] = callback || function () {};
}
},
update: function(id, factory) {
// remove module from cache: ensure get updated moudle when require
delete this.modulesMap[id];
// 重新定义模块
this.define(id, factory);
// call accpet handlers
this.hotUpdateMap[id] && this.hotUpdateMap[id]()
// self accepted
// if (this.selfAcceptedMap[id]) {
// this.require(id)
// }
},
// 重新定义模块,用于获取新的引用
reDefineModule: function(id) {
id = id.replace(/\.js$/i, '');
delete this.modulesMap[id];
var factory = this.factoryMap[id];
if (factory) {
// 重新定义模块
this.define(id, factory)
}
},
depose: function() {
// do nothings
}
}
global.HotModuleReplacementPlugin = HotModuleReplacementPlugin;
})(window);
/* start websocket connection */
(function(global) {
var socket = io.connect('http://{{hostname}}:{{port}}');
var updatedModules = {};
function log() {
var args = [].slice.call(arguments);
args.unshift('[hmr]');
console.log.apply(console, args);
}
socket.on('establish', function (data) {
global.resMap = data.map;
// __M.require.resourceMap(data.map);
log('Hmr server has started!');
});
socket.on('update:js', function(data) {
// log('update: ', data)
var script = document.createElement('script');
script.src = data.uri;
document.head.appendChild(script);
script.onload = function() {
if (data.noReload) {
__M.require(data.moduleId);
} else {
updateFromSelf(data.moduleId);
}
}
});
socket.on('update:css', function(data) {
// log(data)
// var link = document.createElement('link');
// link.href = data.uri;
// link.rel = 'stylesheet';
// link.type = 'text/css';
// document.head.appendChild(link);
var style = document.querySelectorAll('[data-id="' + data.moduleId + '"]');
if (style.length) {
updateStyleTag(style[0], data.content);
} else {
var script = document.head.querySelectorAll('script')
var style = document.createElement('style');
style.setAttribute('data-id', data.moduleId);
updateStyleTag(style, data.content);
if (script.length) {
document.head.insertBefore(style, script[0]);
} else {
document.head.appendChild(style);
}
}
});
socket.on('update:map', function(data) {
global.resMap = data.map;
// __M.require.resourceMap(data.map);
log('resource map has updated!');
});
socket.on('update:reload', function(data) {
reload();
});
function updateStyleTag(style, content) {
try {
style.innerHTML = content;
} catch(e) {
style.styleSheet.cssText = content;
}
}
function getModuleId(id) {
return id.replace(/\.js$/i, '');
}
function getParentModule(map, id) {
var res = map.res;
return Object.keys(res).filter(resId => res[resId]['deps'] && ~res[resId]['deps'].indexOf(id));
}
// hack for vue
// todo: 精准判断
function isTopApp(id, parents) {
// return /page\/(\S+\/)*(App\.vue|app\.vue|index\.js)$/.test(id)
// return /page\/(\S+\/)*index\.js$/.test(id);
return parents.length === 0;
}
function reload() {
log('hmr update failed, page will reload!');
location.reload();
}
// todo: 去重
function updateFromSelf(id) {
var parents = getParentModule(global.resMap, id);
// 清除缓存
__M.hot.data[id] = false;
// 顶层模块
if (isTopApp(id, parents)) {
log('require: ', id);
try {
__M.require(id);
} catch (e) {
var match = typeof e === 'string' && e.match(/\[ModJS\] Cannot find module `(\S+?)`/);
if (match) {
// todo: 后引入的模块暂时无法更新
// __M.require.async(match[1], function() {
// __M.require(id);
// });
// return ;
return reload();
}
console.error(e);
}
return ;
}
// 非顶层模块
parents.forEach(resId => {
// 当前页面用到的模块
if (__M.hot.factoryMap[getModuleId(resId)]) {
log('redefine: ', resId)
__M.hot.reDefineModule(resId);
}
});
parents.forEach(resId => {
// 当前页面用到的模块
if (__M.hot.factoryMap[getModuleId(resId)]) {
log('updateFromSelf: ', resId)
updateFromSelf(resId);
}
});
}
})(window);