mirror of
https://github.com/marko-js/marko.git
synced 2025-12-08 19:26:05 +00:00
191 lines
5.5 KiB
JavaScript
191 lines
5.5 KiB
JavaScript
require('raptor-polyfill/string/endsWith');
|
|
|
|
const nodePath = require('path');
|
|
const fs = require('fs');
|
|
const nodeRequire = require('../node-require');
|
|
|
|
var compiler;
|
|
var marko;
|
|
var runtime;
|
|
var components;
|
|
|
|
var modifiedId = 1;
|
|
var HOT_RELOAD_KEY = Symbol('HOT_RELOAD');
|
|
|
|
function cleaResolvePathCache() {
|
|
var modulePathCache = require('module').Module._pathCache;
|
|
if (!modulePathCache) {
|
|
console.log('[marko/hot-reload] WARNING: Missing: require("module").Module._pathCache [' + __filename + ']');
|
|
return;
|
|
}
|
|
|
|
var keys = Object.keys(modulePathCache);
|
|
keys.forEach(function(key) {
|
|
delete modulePathCache[key];
|
|
});
|
|
}
|
|
|
|
function tryReloadTemplate(path) {
|
|
path = path.replace(/\.js$/, '');
|
|
|
|
try {
|
|
return marko.load(path);
|
|
} catch(e) {
|
|
return undefined;
|
|
}
|
|
}
|
|
|
|
exports.enable = function() {
|
|
if (runtime.__hotReloadEnabled) {
|
|
// Marko has already been monkey-patched. Nothing to do!
|
|
return;
|
|
}
|
|
|
|
runtime.__hotReloadEnabled = true;
|
|
|
|
// We set an environment variable so that _all_ marko modules
|
|
// installed in the project will have hot reload enabled.
|
|
process.env.MARKO_HOT_RELOAD = 'true';
|
|
|
|
function createHotReloadProxy(func, template, methodName) {
|
|
var hotReloadData = template[HOT_RELOAD_KEY];
|
|
if (!hotReloadData) {
|
|
hotReloadData = template[HOT_RELOAD_KEY] = {
|
|
modifiedId: modifiedId,
|
|
latest: template,
|
|
originals: {}
|
|
};
|
|
}
|
|
|
|
hotReloadData.originals[methodName] = func;
|
|
|
|
var templatePath = template.path;
|
|
|
|
function hotReloadProxy() {
|
|
if (hotReloadData.modifiedId !== modifiedId) {
|
|
hotReloadData.modifiedId = modifiedId;
|
|
hotReloadData.latest = tryReloadTemplate(templatePath) || template;
|
|
|
|
if (hotReloadData.latest !== template) {
|
|
template.meta = hotReloadData.latest.meta;
|
|
console.log('[marko/hot-reload] Template successfully reloaded: ' + templatePath);
|
|
}
|
|
}
|
|
|
|
var latest = hotReloadData.latest;
|
|
var originals = latest[HOT_RELOAD_KEY] && latest[HOT_RELOAD_KEY].originals;
|
|
if (!originals) {
|
|
originals = latest;
|
|
}
|
|
|
|
var targetFunc = originals._;
|
|
return targetFunc.apply(latest, arguments);
|
|
}
|
|
|
|
return hotReloadProxy;
|
|
}
|
|
|
|
var oldCreateTemplate = runtime.t;
|
|
|
|
runtime.t = function hotReloadCreateTemplate(path) {
|
|
var originalTemplate = oldCreateTemplate.apply(runtime, arguments);
|
|
var actualRenderFunc;
|
|
|
|
Object.defineProperty(originalTemplate, '_', {
|
|
get: function() {
|
|
return actualRenderFunc;
|
|
},
|
|
|
|
set: function(renderFunc) {
|
|
actualRenderFunc = createHotReloadProxy(renderFunc, originalTemplate, '_');
|
|
}
|
|
});
|
|
|
|
return originalTemplate;
|
|
};
|
|
};
|
|
|
|
/**
|
|
* Checks whether a path ends with a custom Marko extension
|
|
*/
|
|
function _endsWithMarkoExtension(path, requireExtensions) {
|
|
for (var i = 0; i < requireExtensions.length; i++) {
|
|
if (path.endsWith(requireExtensions[i])) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
function normalizeExtension(extension) {
|
|
if (extension.charAt(0) !== '.') {
|
|
extension = '.' + extension;
|
|
}
|
|
return extension;
|
|
}
|
|
|
|
exports.handleFileModified = function(path, options) {
|
|
if (!fs.existsSync(path)) {
|
|
console.log('[marko/hot-reload] WARNING cannot resolve template path: ', path);
|
|
return;
|
|
}
|
|
|
|
options = options || {};
|
|
|
|
// Default hot-reloaded extensions
|
|
var requireExtensions = ['.marko', '.marko.html', '.marko.xml'];
|
|
|
|
if (options.extension) {
|
|
requireExtensions.push(options.extension);
|
|
}
|
|
|
|
if (options.extensions) {
|
|
requireExtensions = requireExtensions.concat(options.extensions);
|
|
}
|
|
|
|
var nodeRequireExtensions = nodeRequire.getExtensions();
|
|
if (nodeRequireExtensions) {
|
|
requireExtensions = requireExtensions.concat(nodeRequireExtensions);
|
|
}
|
|
|
|
for (var i = 0; i < requireExtensions.length; i++) {
|
|
requireExtensions[i] = normalizeExtension(requireExtensions[i]);
|
|
}
|
|
|
|
var basename = nodePath.basename(path);
|
|
|
|
function handleFileModified() {
|
|
console.log('[marko/hot-reload] File modified: ' + path);
|
|
runtime.cache = {};
|
|
compiler.clearCaches();
|
|
cleaResolvePathCache();
|
|
modifiedId++;
|
|
}
|
|
|
|
if (basename === 'marko-tag.json' || basename === 'marko.json') {
|
|
handleFileModified();
|
|
// If we taglib was modified then uncache *all* templates so that they will
|
|
// all be reloaded
|
|
Object.keys(require.cache).forEach((filename) => {
|
|
if (filename.endsWith('.marko') || filename.endsWith('.marko.js')) {
|
|
delete require.cache[filename];
|
|
}
|
|
});
|
|
} else if (_endsWithMarkoExtension(path, requireExtensions)) {
|
|
handleFileModified();
|
|
delete require.cache[path];
|
|
delete require.cache[path + '.js'];
|
|
} else if (basename === 'component.js') {
|
|
handleFileModified();
|
|
var dir = nodePath.dirname(path);
|
|
var templatePath = nodePath.join(dir, 'index.marko');
|
|
delete require.cache[path];
|
|
delete require.cache[templatePath];
|
|
delete require.cache[templatePath + '.js'];
|
|
}
|
|
};
|
|
|
|
compiler = require('../compiler');
|
|
marko = require('../');
|
|
runtime = require('../runtime/html');
|
|
components = require('../components'); |