es-dev-server
Version:
Development server for modern web apps
111 lines • 4.06 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.addToCache = exports.tryServeFromCache = exports.createResponseBodyCache = void 0;
const tslib_1 = require("tslib");
const fs_1 = tslib_1.__importDefault(require("fs"));
const lru_cache_1 = tslib_1.__importDefault(require("lru-cache"));
const util_1 = require("util");
const utils_1 = require("./utils/utils");
const stat = util_1.promisify(fs_1.default.stat);
/**
* Cache by user agent + file path, so that there can be unique transforms
* per browser.
*/
function createCacheKey(context) {
return `${context.get('user-agent')}${context.url}`;
}
async function getLastModified(path) {
try {
return (await stat(path)).mtimeMs;
}
catch (error) {
return -1;
}
}
function createResponseBodyCache(cfg, fileWatcher) {
/** @type {Map<String, String>} */
const cacheKeysForFilePaths = new Map();
const cache = new lru_cache_1.default({
length: (e, key) => e.body.length + (key ? key.length : 0),
max: 52428800,
// don't call dispose on overwriting
noDisposeOnSet: true,
// remove file path -> url mapping when we are no longer caching it
dispose(cacheKey) {
for (const [filePath, cacheKeyForFilePath] of cacheKeysForFilePaths.entries()) {
if (cacheKeyForFilePath === cacheKey) {
cacheKeysForFilePaths.delete(filePath);
return;
}
}
},
});
// remove file from cache on change
fileWatcher.addListener('change', e => {
const cacheKey = cacheKeysForFilePaths.get(e);
if (cacheKey) {
cache.del(cacheKey);
}
});
return { cache, cacheKeysForFilePaths };
}
exports.createResponseBodyCache = createResponseBodyCache;
async function tryServeFromCache(cache, context) {
/** @type {string} */
let servingFromCache = false;
const cacheKey = createCacheKey(context);
const cached = cache.get(cacheKey);
if (cached) {
// we watch files, and remove them on change, but there can be edge cases
// where these events do not come through properly (the file system is a 'live' system)
// we double check the last modified timestamp first
if (cached.lastModified === (await getLastModified(cached.filePath))) {
context.body = cached.body;
context.response.set(cached.headers);
context.status = 200;
servingFromCache = true;
utils_1.logDebug(`Serving from response body cache: ${context.url}`);
}
else {
// remove file from cache if it changed in the meantime, and serve regularly
cache.del(cacheKey);
}
}
return { cacheKey, context, cached: servingFromCache, cachedBody: cached === null || cached === void 0 ? void 0 : cached.body };
}
exports.tryServeFromCache = tryServeFromCache;
async function addToCache(config) {
const { cache, cacheKey, cacheKeysForFilePaths, context, cfg } = config;
if (context.method !== 'GET' || !context.body) {
return;
}
if (context.status !== 200) {
return;
}
if (utils_1.isGeneratedFile(context.url) || !context.response.is('js')) {
return;
}
try {
const body = await utils_1.getBodyAsString(context);
const filePath = utils_1.getRequestFilePath(context, cfg.rootDir);
if (!filePath) {
return;
}
cacheKeysForFilePaths.set(filePath, context.url);
cache.set(cacheKey, {
body,
headers: context.response.headers,
filePath,
lastModified: await getLastModified(filePath),
});
utils_1.logDebug(`Adding to response body cache: ${context.url}`);
}
catch (error) {
if (error instanceof utils_1.RequestCancelledError) {
return;
}
throw error;
}
}
exports.addToCache = addToCache;
//# sourceMappingURL=response-body-cache.js.map