kopia lustrzana https://github.com/c9/core
use caches api for faster reloading in dev mode
rodzic
ef3d6d3db8
commit
054bddd577
|
@ -36,6 +36,14 @@ var define = function(name, deps, callback) {
|
||||||
deps = null;
|
deps = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (nextModule) {
|
||||||
|
if (!name || name == nextModule.name) {
|
||||||
|
name = nextModule.name;
|
||||||
|
deps = deps || nextModule.deps;
|
||||||
|
nextModule = null;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (!name)
|
if (!name)
|
||||||
return defQueue.push([deps, callback]);
|
return defQueue.push([deps, callback]);
|
||||||
|
|
||||||
|
@ -60,6 +68,7 @@ var define = function(name, deps, callback) {
|
||||||
define.lastModule = name;
|
define.lastModule = name;
|
||||||
};
|
};
|
||||||
var defQueue = [];
|
var defQueue = [];
|
||||||
|
var nextModule;
|
||||||
var addToLoadQueue = function(missing, deps, callback, errback) {
|
var addToLoadQueue = function(missing, deps, callback, errback) {
|
||||||
var toLoad = missing.length;
|
var toLoad = missing.length;
|
||||||
var map = {};
|
var map = {};
|
||||||
|
@ -275,12 +284,18 @@ var config = require.config = function(cfg) {
|
||||||
config.paths[p] = cfg.paths[p];
|
config.paths[p] = cfg.paths[p];
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (cfg.useCache && global.caches) {
|
||||||
|
config.useCache = true;
|
||||||
|
checkCache();
|
||||||
|
}
|
||||||
|
|
||||||
if (cfg.baseUrlLoadBalancers)
|
if (cfg.baseUrlLoadBalancers)
|
||||||
config.baseUrlLoadBalancers = cfg.baseUrlLoadBalancers;
|
config.baseUrlLoadBalancers = cfg.baseUrlLoadBalancers;
|
||||||
};
|
};
|
||||||
config.packages = Object.create(null);
|
config.packages = Object.create(null);
|
||||||
config.paths = Object.create(null);
|
config.paths = Object.create(null);
|
||||||
config.baseUrl = "";
|
config.baseUrl = "";
|
||||||
|
config.useCache = false;
|
||||||
|
|
||||||
require.undef = function(module, recursive) {
|
require.undef = function(module, recursive) {
|
||||||
if (recursive) {
|
if (recursive) {
|
||||||
|
@ -347,7 +362,7 @@ require.toUrl = function(moduleName, ext, skipExt, skipBalancers) {
|
||||||
if (!absRe.test(url)) {
|
if (!absRe.test(url)) {
|
||||||
url = (config.baseUrl || require.MODULE_LOAD_URL + "/") + url;
|
url = (config.baseUrl || require.MODULE_LOAD_URL + "/") + url;
|
||||||
}
|
}
|
||||||
if (url[0] === "/" && config.baseUrlLoadBalancers && !skipBalancers) {
|
if (url[0] === "/" && config.baseUrlLoadBalancers && !skipBalancers && !config.useCache) {
|
||||||
var n = Math.abs(hashCode(url)) % config.baseUrlLoadBalancers.length;
|
var n = Math.abs(hashCode(url)) % config.baseUrlLoadBalancers.length;
|
||||||
url = config.baseUrlLoadBalancers[n] + url;
|
url = config.baseUrlLoadBalancers[n] + url;
|
||||||
}
|
}
|
||||||
|
@ -365,7 +380,7 @@ function hashCode(string) {
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
var loadScript = function(path, id, callback) {
|
var loadScriptWithTag = function(path, id, callback) {
|
||||||
// TODO use importScripts for webworkers
|
// TODO use importScripts for webworkers
|
||||||
var head = document.head || document.documentElement;
|
var head = document.head || document.documentElement;
|
||||||
var s = document.createElement("script");
|
var s = document.createElement("script");
|
||||||
|
@ -373,7 +388,7 @@ var loadScript = function(path, id, callback) {
|
||||||
s.charset = "utf-8";
|
s.charset = "utf-8";
|
||||||
s.async = true;
|
s.async = true;
|
||||||
|
|
||||||
if (path.lastIndexOf(require.MODULE_LOAD_URL, 0) == 0)
|
if (path.lastIndexOf(require.MODULE_LOAD_URL, 0) == 0 && path[0] != "/")
|
||||||
s.crossOrigin = true;
|
s.crossOrigin = true;
|
||||||
|
|
||||||
head.appendChild(s);
|
head.appendChild(s);
|
||||||
|
@ -393,6 +408,137 @@ var loadScript = function(path, id, callback) {
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
function loadText(path, cb) {
|
||||||
|
var xhr = new window.XMLHttpRequest();
|
||||||
|
xhr.open("GET", path, true);
|
||||||
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
|
xhr.onload = function(e) { cb(null, xhr.responseText, xhr); };
|
||||||
|
xhr.onabort = xhr.onerror = function(e) { cb(e); };
|
||||||
|
xhr.send("");
|
||||||
|
}
|
||||||
|
|
||||||
|
/*** cache ***/
|
||||||
|
var host = location.protocol + "//" + location.hostname + (location.port ? ":" + location.port : "");
|
||||||
|
var loadScript = function(path, id, callback) {
|
||||||
|
if (!config.useCache)
|
||||||
|
return loadScriptWithTag(path, id, callback);
|
||||||
|
if (!/https?:/.test(path))
|
||||||
|
path = host + path;
|
||||||
|
var cb = function(e, val, deps) {
|
||||||
|
if (e) console.error("Couldn't load module " + module, e);
|
||||||
|
|
||||||
|
nextModule = {
|
||||||
|
name: id,
|
||||||
|
deps: deps
|
||||||
|
};
|
||||||
|
window.eval(val + "\n//# sourceURL=" + path);
|
||||||
|
callback(null, id);
|
||||||
|
return define.loaded[id];
|
||||||
|
};
|
||||||
|
loadCached(path, cb);
|
||||||
|
};
|
||||||
|
|
||||||
|
var loadCached = function(path, cb) {
|
||||||
|
if (!config.useCache)
|
||||||
|
return loadText(path, cb);
|
||||||
|
function loadNew() {
|
||||||
|
loadText(path, function(e, val, xhr) {
|
||||||
|
var m = cb(e, val);
|
||||||
|
if (val) {
|
||||||
|
var ETAG = xhr.getResponseHeader("ETAG");
|
||||||
|
var res = new Response(val);
|
||||||
|
res.headers.set("ETAG", ETAG);
|
||||||
|
var req = new Request(path);
|
||||||
|
req.headers.set("ETAG", ETAG);
|
||||||
|
if (m && m.deps)
|
||||||
|
res.headers.set("deps", m.deps.join(","));
|
||||||
|
ideCache.put(req, res).catch(function() {
|
||||||
|
ideCache.delete(path);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
if (ideCachePromiss) {
|
||||||
|
return ideCachePromiss.then(function(i) {
|
||||||
|
if (i) ideCache = i;
|
||||||
|
loadCached(path, cb);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
ideCache.match(path).then(function(e) {
|
||||||
|
if (!e)
|
||||||
|
return loadNew();
|
||||||
|
e.text().then(function(val) {
|
||||||
|
var deps = e.headers.get("deps");
|
||||||
|
if (typeof deps == "string")
|
||||||
|
deps = deps ? deps.split(",") : [];
|
||||||
|
|
||||||
|
cb(null, val, deps);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
var ideCache;
|
||||||
|
var ideCachePromiss;
|
||||||
|
function checkCache() {
|
||||||
|
var baseUrl;
|
||||||
|
ideCachePromiss = config.useCache && window.caches.open("ide").then(function(ideCache_) {
|
||||||
|
ideCache = ideCache_;
|
||||||
|
return ideCache.keys();
|
||||||
|
}).then(function(keys) {
|
||||||
|
baseUrl = host + config.baseUrl;
|
||||||
|
var val = keys.map(function(r) {
|
||||||
|
var url = r.url;
|
||||||
|
if (url.startsWith(baseUrl))
|
||||||
|
url = url.slice(baseUrl.length);
|
||||||
|
return r.headers.get("etag") + " " + url;
|
||||||
|
}).join("\n") + "\n";
|
||||||
|
if (val.length == 1) {
|
||||||
|
ideCachePromiss = null;
|
||||||
|
return ideCache;
|
||||||
|
}
|
||||||
|
return new Promise(function(resolve) {
|
||||||
|
var checked = 0;
|
||||||
|
var buffer = "";
|
||||||
|
var toDelete = []
|
||||||
|
post("/static/__check__", val, function(t) {
|
||||||
|
var e = t.slice(checked);
|
||||||
|
checked = t.length;
|
||||||
|
var parts = (buffer + e).split("\n");
|
||||||
|
buffer = parts.pop();
|
||||||
|
for (var i = 0; i < parts.length; i++) {
|
||||||
|
if (parts[i]) {
|
||||||
|
var del = ideCache.delete(baseUrl + parts[i]);
|
||||||
|
toDelete.push(del);
|
||||||
|
console.log(parts[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, function(e, t) {
|
||||||
|
ideCachePromiss = null;
|
||||||
|
Promise.all(toDelete).then(function() {
|
||||||
|
resolve(ideCache);
|
||||||
|
});
|
||||||
|
setTimeout(function() {
|
||||||
|
setTimeout(function() {
|
||||||
|
// TODO for now we do not support checking second time so we unset useCache after a while
|
||||||
|
config.useCache = false;
|
||||||
|
}, 5000);
|
||||||
|
}, 5000);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
return ideCachePromiss;
|
||||||
|
}
|
||||||
|
|
||||||
|
function post(path, val, progress, cb) {
|
||||||
|
var xhr = new window.XMLHttpRequest();
|
||||||
|
xhr.open("POST", path, true);
|
||||||
|
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
||||||
|
xhr.onload = function(e) { cb(null, xhr.responseText, xhr); };
|
||||||
|
xhr.onreadystatechange = function(e) { progress(xhr.responseText, xhr); };
|
||||||
|
xhr.onabort = xhr.onerror = function(e) { cb(e); };
|
||||||
|
xhr.send(val);
|
||||||
|
}
|
||||||
|
|
||||||
require.load = function(module) {
|
require.load = function(module) {
|
||||||
var i = module.indexOf("!") + 1;
|
var i = module.indexOf("!") + 1;
|
||||||
if (i) {
|
if (i) {
|
||||||
|
@ -447,12 +593,19 @@ require["text!"] = function(module, callback) {
|
||||||
define("text!" + module, [], val);
|
define("text!" + module, [], val);
|
||||||
callback();
|
callback();
|
||||||
};
|
};
|
||||||
var xhr = new window.XMLHttpRequest();
|
loadCached(url, cb);
|
||||||
xhr.open("GET", url + "?access_token=fake_token", true);
|
};
|
||||||
xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");
|
require["ace/requirejs/text!"] = function(module, callback) {
|
||||||
xhr.onload = function(e) { cb(null, xhr.responseText); };
|
var url = require.toUrl(module);
|
||||||
xhr.onabort = xhr.onerror = function(e) { cb(e); };
|
if (define.fetchedUrls[url] & 2)
|
||||||
xhr.send("");
|
return false;
|
||||||
|
define.fetchedUrls[url] |= 2;
|
||||||
|
var cb = function(e, val) {
|
||||||
|
if (e) console.error("Couldn't load module " + module, e);
|
||||||
|
define("ace/requirejs/text!" + module, [], val);
|
||||||
|
callback();
|
||||||
|
};
|
||||||
|
loadCached(url, cb);
|
||||||
};
|
};
|
||||||
|
|
||||||
/*** add global define ***/
|
/*** add global define ***/
|
||||||
|
|
|
@ -51,15 +51,13 @@ define(function(require, exports, module) {
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
var url = themePrefix + "/" + name + ".css";
|
var url = themePrefix + "/" + name + ".css";
|
||||||
http.request(url, {
|
require(["text!" + url], function(data) {
|
||||||
timeout: 2 * 60 * 1000
|
|
||||||
}, function(err, data) {
|
|
||||||
if (err)
|
|
||||||
return callback(err, data);
|
|
||||||
// set sourceurl so that sourcemaps work when theme is inserted as a style tag
|
// set sourceurl so that sourcemaps work when theme is inserted as a style tag
|
||||||
data += "\n/*# sourceURL=" + url + " */";
|
data += "\n/*# sourceURL=" + url + " */";
|
||||||
themes[name] = data;
|
themes[name] = data;
|
||||||
callback(err, data);
|
callback(null, data);
|
||||||
|
}, function(err) {
|
||||||
|
callback(err);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
main.consumes = [
|
main.consumes = [
|
||||||
"connect",
|
"connect",
|
||||||
"connect.cors",
|
"connect.cors",
|
||||||
|
"connect.static",
|
||||||
"cdn.build"
|
"cdn.build"
|
||||||
];
|
];
|
||||||
main.provides = [];
|
main.provides = [];
|
||||||
|
@ -12,6 +13,7 @@ module.exports = main;
|
||||||
function main(options, imports, register) {
|
function main(options, imports, register) {
|
||||||
var connect = imports.connect;
|
var connect = imports.connect;
|
||||||
var build = imports["cdn.build"];
|
var build = imports["cdn.build"];
|
||||||
|
var connectStatic = imports["connect.static"];
|
||||||
|
|
||||||
var fs = require("fs");
|
var fs = require("fs");
|
||||||
var path = require("path");
|
var path = require("path");
|
||||||
|
@ -28,6 +30,120 @@ function main(options, imports, register) {
|
||||||
|
|
||||||
var section = api.section("static");
|
var section = api.section("static");
|
||||||
|
|
||||||
|
var resolveModulePath = require("architect-build/module-deps").resolveModulePath;
|
||||||
|
connectStatic.getRequireJsConfig().useCache = options.useBrowserCache;
|
||||||
|
section.post("/__check__", [function(req, res, next) {
|
||||||
|
req.params.hash = "any";
|
||||||
|
next();
|
||||||
|
}, prepare, function(req, res, next) {
|
||||||
|
function FsQ() {
|
||||||
|
this.buffer = [];
|
||||||
|
this.process = function() {};
|
||||||
|
this.active = 0;
|
||||||
|
this.ended = false;
|
||||||
|
this.maxActive = 100;
|
||||||
|
}
|
||||||
|
FsQ.prototype.write = function(arr) {
|
||||||
|
this.buffer.push.apply(this.buffer, arr);
|
||||||
|
this.take();
|
||||||
|
};
|
||||||
|
FsQ.prototype.take = function(arr) {
|
||||||
|
while (this.buffer.length && this.active < this.maxActive) {
|
||||||
|
this.process(this.buffer.pop());
|
||||||
|
this.active++;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
FsQ.prototype.oneDone = function() {
|
||||||
|
this.active--;
|
||||||
|
if (!this.active)
|
||||||
|
this.take();
|
||||||
|
if (!this.active && this.ended)
|
||||||
|
this.end();
|
||||||
|
};
|
||||||
|
|
||||||
|
res.writeHead(200, {
|
||||||
|
"Content-Type": "application/javascript",
|
||||||
|
"Cache-Control": "no-cache, no-store"
|
||||||
|
});
|
||||||
|
req.setEncoding("utf8");
|
||||||
|
|
||||||
|
var buffer = "";
|
||||||
|
var t = Date.now();
|
||||||
|
var q = new FsQ();
|
||||||
|
var skinChanged = false;
|
||||||
|
var requestedSkin = "";
|
||||||
|
var requestedSkinChanged = false;
|
||||||
|
q.process = function(e) {
|
||||||
|
var parts = e.split(" ");
|
||||||
|
var id = parts[1];
|
||||||
|
var etag = parts[0];
|
||||||
|
var path = resolveModulePath(id, req.pathConfig.pathMap);
|
||||||
|
|
||||||
|
if (path == id && !/^(\/|\w:)/.test(path)) {
|
||||||
|
path = build.cacheDir + "/" + path;
|
||||||
|
if (/^\w+\/skin\//.test(id))
|
||||||
|
requestedSkin = id;
|
||||||
|
}
|
||||||
|
|
||||||
|
fs.stat(path, function(err, s) {
|
||||||
|
if (!err) {
|
||||||
|
var mt = s.mtime.valueOf();
|
||||||
|
var etagNew = '"' + s.size +"-" + mt + '"';
|
||||||
|
if (etag !== etagNew) {
|
||||||
|
err = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (err) {
|
||||||
|
if (!skinChanged && /\.(css|less)/.test(id))
|
||||||
|
skinChanged = true;
|
||||||
|
if (requestedSkin == id)
|
||||||
|
requestedSkinChanged = true;
|
||||||
|
res.write(id + "\n");
|
||||||
|
}
|
||||||
|
q.oneDone();
|
||||||
|
});
|
||||||
|
};
|
||||||
|
q.end = function() {
|
||||||
|
if (!q.buffer.length && !q.active) {
|
||||||
|
console.log(skinChanged, requestedSkinChanged, requestedSkin)
|
||||||
|
if (skinChanged && !requestedSkinChanged && requestedSkin) {
|
||||||
|
res.write(requestedSkin + "\n");
|
||||||
|
console.info("Deleting old skin", requestedSkin);
|
||||||
|
return fs.unlink(build.cacheDir + "/" + requestedSkin, function() {
|
||||||
|
skinChanged = false;
|
||||||
|
q.end();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
res.write("\n");
|
||||||
|
res.end();
|
||||||
|
console.info("Checking cache state took:", t - Date.now(), "ms");
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
q.ended = true;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
function onData(e) {
|
||||||
|
var parts = (buffer + e).split("\n");
|
||||||
|
buffer = parts.pop();
|
||||||
|
q.write(parts);
|
||||||
|
// console.log(i++);
|
||||||
|
}
|
||||||
|
function onEnd(e) {
|
||||||
|
console.log("end", t - Date.now());
|
||||||
|
q.end();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (req.body) {
|
||||||
|
// TODO disable automatic buffering in connect
|
||||||
|
onData(Object.keys(req.body)[0]);
|
||||||
|
onEnd();
|
||||||
|
} else {
|
||||||
|
req.on("end", onEnd);
|
||||||
|
req.on("data", onData);
|
||||||
|
}
|
||||||
|
}]);
|
||||||
|
|
||||||
// section.use(foreverCache());
|
// section.use(foreverCache());
|
||||||
section.use(imports["connect.cors"].cors("*"));
|
section.use(imports["connect.cors"].cors("*"));
|
||||||
section.use(connect.getModule().compress());
|
section.use(connect.getModule().compress());
|
||||||
|
@ -103,6 +219,8 @@ function main(options, imports, register) {
|
||||||
type = "text/css";
|
type = "text/css";
|
||||||
|
|
||||||
res.setHeader("Content-Type", type);
|
res.setHeader("Content-Type", type);
|
||||||
|
var mtime = Date.now();
|
||||||
|
res.setHeader("ETAG", '"' + Buffer.byteLength(code) + "-" + mtime + '"');
|
||||||
res.statusCode = 200;
|
res.statusCode = 200;
|
||||||
res.end(code);
|
res.end(code);
|
||||||
|
|
||||||
|
@ -114,9 +232,11 @@ function main(options, imports, register) {
|
||||||
|
|
||||||
atomic.writeFile(filename, code, "utf8", function(err) {
|
atomic.writeFile(filename, code, "utf8", function(err) {
|
||||||
if (err)
|
if (err)
|
||||||
console.error("Caching file", filename, "failed", err);
|
return console.error("Caching file", filename, "failed", err);
|
||||||
else
|
|
||||||
console.log("File cached at", filename);
|
console.log("File cached at", filename);
|
||||||
|
// set utime to have consistent etag
|
||||||
|
fs.utimes(filename, mtime, mtime, function() {});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
Ładowanie…
Reference in New Issue