UNPKG

reboost

Version:

A super fast dev server for rapid web development

168 lines (167 loc) 8.24 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.createFileHandler = void 0; const tslib_1 = require("tslib"); const md5_file_1 = (0, tslib_1.__importDefault)(require("md5-file")); const chalk_1 = (0, tslib_1.__importDefault)(require("chalk")); const fs_1 = (0, tslib_1.__importDefault)(require("fs")); const path_1 = (0, tslib_1.__importDefault)(require("path")); const crypto_1 = (0, tslib_1.__importDefault)(require("crypto")); const utils_1 = require("./utils"); const transformer_1 = require("./transformer"); const watcher_1 = require("./watcher"); const sourceMapCommentRE = /^[ \t]*\/\/#\s*sourceMappingURL=.+(?![\s\S]*\/\/#\s*sourceMappingURL=.+)/m; const fixSourceMap = (code, sourceMapPath) => { // Remove other source maps return `${code.replace(sourceMapCommentRE, '')}\n//# sourceMappingURL=/raw?q=${encodeURIComponent(sourceMapPath)}`; }; const createFileHandler = (instance) => { let initialized = false; let filesDir; let transformer; let watcher; let currentPlugins; const { config, cache } = instance; const memoizedFiles = new Map(); const noop = () => { }; const eTagBase = ` mtime: "@mtime" plugins: "@plugins" dependentsMtime: "@dependentsMtime" `.split('\n').map((s) => s.trim()).join('\n').trim(); const getETag = (filePath) => { const mtime = Math.floor(fs_1.default.statSync(filePath).mtimeMs) + ''; let dependentsMtime = ''; const deps = cache.cacheInfo[cache.cacheIDs[filePath]].dependencies; if (deps) { dependentsMtime = Object.keys(deps).sort().map((dependency) => { try { return Math.floor(fs_1.default.statSync(dependency).mtimeMs); } catch (e) { return ''; } }).join('-'); } const eTagStr = eTagBase .replace('@mtime', mtime) .replace('@plugins', currentPlugins) .replace('@dependentsMtime', dependentsMtime); const eTag = crypto_1.default.createHash('md5').update(eTagStr).digest('hex'); return eTag; }; return async (ctx) => { if (!initialized) { filesDir = cache.getFilesDir(); transformer = (0, transformer_1.createTransformer)(instance); watcher = (0, watcher_1.createWatcher)(instance); currentPlugins = cache.getCurrentPlugins(); (0, utils_1.ensureDir)(config.cacheDir); (0, utils_1.ensureDir)(filesDir); fs_1.default.writeFile(path_1.default.join(config.cacheDir, './.gitignore'), '/**/*', noop); initialized = true; } const filePath = ctx.query.q; let startTime; let transformedCode; if (instance.isLogEnabled('responseTime')) startTime = process.hrtime(); if (fs_1.default.existsSync(filePath)) { const mtime = Math.floor(fs_1.default.statSync(filePath).mtimeMs); const makeNewCache = async () => { const cacheID = (config.debugMode ? path_1.default.basename(filePath) + '-' : '') + (0, utils_1.uniqueID)(16); const { code, map, dependencies, error } = await transformer.transformFile(filePath); transformedCode = code; if (map) { const sourceMapPath = cache.sourceMapPath(cacheID); transformedCode = fixSourceMap(transformedCode, sourceMapPath); fs_1.default.writeFile(sourceMapPath, map, noop); } if (!error) { fs_1.default.writeFileSync(cache.cacheFilePath(cacheID), transformedCode); cache.cacheIDs[filePath] = cacheID; cache.cacheInfo[cacheID] = { hash: await (0, md5_file_1.default)(filePath), mtime, plugins: currentPlugins }; await cache.updateDependencies(filePath, dependencies, true); cache.saveData(); if (config.cacheOnMemory) memoizedFiles.set(filePath, transformedCode); ctx.set('ETag', getETag(filePath)); } watcher.setDependencies(filePath, dependencies); }; if (cache.cacheIDs[filePath]) { const cacheID = cache.cacheIDs[filePath]; const cacheInfo = cache.cacheInfo[cacheID]; const cacheFilePath = cache.cacheFilePath(cacheID); let hash; try { if (cacheInfo.plugins !== currentPlugins || await cache.hasDependenciesChanged(filePath) || ((cacheInfo.mtime !== mtime) && (cacheInfo.hash !== (hash = await (0, md5_file_1.default)(filePath))))) { const { code, map, dependencies, error } = await transformer.transformFile(filePath); transformedCode = code; if (map) { const sourceMapPath = cache.sourceMapPath(cacheID); transformedCode = fixSourceMap(transformedCode, sourceMapPath); fs_1.default.writeFile(sourceMapPath, map, noop); } if (!error) { fs_1.default.writeFileSync(cacheFilePath, transformedCode); cacheInfo.hash = hash || await (0, md5_file_1.default)(filePath); cacheInfo.mtime = mtime; cacheInfo.plugins = currentPlugins; await cache.updateDependencies(filePath, dependencies); cache.saveData(); if (config.cacheOnMemory) memoizedFiles.set(filePath, transformedCode); ctx.set('ETag', getETag(filePath)); } watcher.setDependencies(filePath, dependencies); } else { if (ctx.get('If-None-Match') === getETag(filePath)) { ctx.status = 304; } else { transformedCode = config.cacheOnMemory && memoizedFiles.get(filePath) || fs_1.default.readFileSync(cacheFilePath, 'utf8'); if (cacheInfo.mtime !== mtime) { cacheInfo.mtime = mtime; cache.saveData(); } if (config.cacheOnMemory) memoizedFiles.set(filePath, transformedCode); } watcher.setDependencies(filePath, Object.keys(cacheInfo.dependencies || {})); } } catch (e) { if (e.message.includes('ENOENT')) { await makeNewCache(); } console.error(e); } } else { await makeNewCache(); } } else { const message = `[reboost] The requested file does not exist: ${filePath}.`; transformedCode = `console.error(${JSON.stringify(message)})`; } if (transformedCode) { ctx.type = 'text/javascript'; ctx.body = transformedCode; } if (instance.isLogEnabled('responseTime')) { const endTime = process.hrtime(startTime); instance.log('responseTime', chalk_1.default.cyan(`Response time - ${(0, utils_1.toPosix)(path_1.default.relative(config.rootDir, filePath))}:`), (0, utils_1.getReadableHRTime)(endTime)); } }; }; exports.createFileHandler = createFileHandler;