UNPKG

5.35 kBJavaScriptView Raw
1/* eslint-disable no-console */
2
3const nodePath = require("path");
4const fs = require("fs");
5const nodeRequire = require("./node-require");
6
7var compiler;
8var marko;
9var runtime;
10
11var modifiedId = 1;
12var HOT_RELOAD_KEY = Symbol("HOT_RELOAD");
13
14function cleaResolvePathCache() {
15 var modulePathCache = require("module").Module._pathCache;
16 if (!modulePathCache) {
17 console.log('[marko/hot-reload] WARNING: Missing: require("module").Module._pathCache [' + __filename + "]");
18 return;
19 }
20
21 var keys = Object.keys(modulePathCache);
22 keys.forEach(function (key) {
23 delete modulePathCache[key];
24 });
25}
26
27function tryReloadTemplate(path) {
28 path = path.replace(/\.js$/, "");
29
30 try {
31 if (require.extensions[".marko"]) {
32 return require(path);
33 }
34
35 return marko.load(path);
36 } catch (e) {
37 return undefined;
38 }
39}
40
41exports.enable = function (options) {
42 if (runtime.__hotReloadEnabled) {
43 // Marko has already been monkey-patched. Nothing to do!
44 return;
45 }
46
47 options = options || {};
48 var silent = options.silent || false;
49
50 runtime.__hotReloadEnabled = true;
51
52 // We set an environment variable so that _all_ marko modules
53 // installed in the project will have hot reload enabled.
54 process.env.MARKO_HOT_RELOAD = "true";
55
56 function createHotReloadProxy(func, template, methodName) {
57 var hotReloadData = template[HOT_RELOAD_KEY];
58 if (!hotReloadData) {
59 hotReloadData = template[HOT_RELOAD_KEY] = {
60 modifiedId: modifiedId,
61 latest: template,
62 originals: {}
63 };
64 }
65
66 hotReloadData.originals[methodName] = func;
67
68 var templatePath = template.path;
69
70 function hotReloadProxy() {
71 if (hotReloadData.modifiedId !== modifiedId) {
72 hotReloadData.modifiedId = modifiedId;
73 hotReloadData.latest = tryReloadTemplate(templatePath) || template;
74
75 if (hotReloadData.latest !== template) {
76 template.meta = hotReloadData.latest.meta;
77 if (!silent) {
78 console.log("[marko/hot-reload] Template successfully reloaded: " + templatePath);
79 }
80 }
81 }
82
83 var latest = hotReloadData.latest;
84 var originals = latest[HOT_RELOAD_KEY] && latest[HOT_RELOAD_KEY].originals;
85 if (!originals) {
86 originals = latest;
87 }
88
89 var targetFunc = originals._;
90 return targetFunc.apply(latest, arguments);
91 }
92
93 return hotReloadProxy;
94 }
95
96 var oldCreateTemplate = runtime.t;
97
98 runtime.t = function hotReloadCreateTemplate() {
99 var originalTemplate = oldCreateTemplate.apply(runtime, arguments);
100 var actualRenderFunc;
101
102 Object.defineProperty(originalTemplate, "_", {
103 get: function () {
104 return actualRenderFunc;
105 },
106
107 set: function (renderFunc) {
108 actualRenderFunc = createHotReloadProxy(renderFunc, originalTemplate, "_");
109 }
110 });
111
112 return originalTemplate;
113 };
114};
115
116/**
117 * Checks whether a path ends with a custom Marko extension
118 */
119function _endsWithMarkoExtension(path, requireExtensions) {
120 for (var i = 0; i < requireExtensions.length; i++) {
121 if (path.endsWith(requireExtensions[i])) {
122 return true;
123 }
124 }
125 return false;
126}
127
128function normalizeExtension(extension) {
129 if (extension.charAt(0) !== ".") {
130 extension = "." + extension;
131 }
132 return extension;
133}
134
135exports.handleFileModified = function (path, options) {
136 if (!fs.existsSync(path)) {
137 console.log("[marko/hot-reload] WARNING cannot resolve template path: ", path);
138 return;
139 }
140
141 options = options || {};
142 var silent = options.silent || false;
143
144 // Default hot-reloaded extensions
145 var requireExtensions = [".marko", ".marko.html", ".marko.xml"];
146
147 if (options.extension) {
148 requireExtensions.push(options.extension);
149 }
150
151 if (options.extensions) {
152 requireExtensions = requireExtensions.concat(options.extensions);
153 }
154
155 var nodeRequireExtensions = nodeRequire.getExtensions();
156 if (nodeRequireExtensions) {
157 requireExtensions = requireExtensions.concat(nodeRequireExtensions);
158 }
159
160 for (var i = 0; i < requireExtensions.length; i++) {
161 requireExtensions[i] = normalizeExtension(requireExtensions[i]);
162 }
163
164 var basename = nodePath.basename(path);
165
166 function handleFileModified() {
167 if (!silent) {
168 console.log("[marko/hot-reload] File modified: " + path);
169 }
170 runtime.cache = {};
171 compiler.clearCaches();
172 cleaResolvePathCache();
173 modifiedId++;
174 }
175
176 if (basename === "marko-tag.json" || basename === "marko.json") {
177 handleFileModified();
178 // If we taglib was modified then uncache *all* templates so that they will
179 // all be reloaded
180 Object.keys(require.cache).forEach(filename => {
181 if (filename.endsWith(".marko") || filename.endsWith(".marko.js")) {
182 delete require.cache[filename];
183 }
184 });
185 } else if (_endsWithMarkoExtension(path, requireExtensions)) {
186 handleFileModified();
187 delete require.cache[path];
188 delete require.cache[path + ".js"];
189 } else if (basename === "component.js") {
190 handleFileModified();
191 var dir = nodePath.dirname(path);
192 var templatePath = nodePath.join(dir, "index.marko");
193 delete require.cache[path];
194 delete require.cache[templatePath];
195 delete require.cache[templatePath + ".js"];
196 }
197};
198
199compiler = require("./compiler");
200marko = require("./");
201runtime = require("./runtime/html");
\No newline at end of file