UNPKG

@farmfe/core

Version:

Farm is a extremely fast web build tool written in Rust. Farm can start a project in milliseconds and perform HMR within 10ms, making it much faster than similar tools like webpack and vite.

134 lines 5.71 kB
import fse from 'fs-extra'; // queue all updates and compile them one by one import { stat } from 'node:fs/promises'; import { isAbsolute, relative } from 'node:path'; import { checkClearScreen } from '../config/index.js'; import { bold, cyan, getDynamicResources, green } from '../utils/index.js'; import { logError } from './error.js'; export class HmrEngine { constructor(compiler, devServer, _logger) { this._logger = _logger; this._updateQueue = []; this.recompileAndSendResult = async () => { const queue = [...this._updateQueue]; if (queue.length === 0) { return; } let updatedFilesStr = queue .map((item) => { if (isAbsolute(item)) { return relative(this._compiler.config.config.root, item); } else { const resolvedPath = this._compiler.transformModulePath(this._compiler.config.config.root, item); return relative(this._compiler.config.config.root, resolvedPath); } }) .join(', '); if (updatedFilesStr.length >= 100) { updatedFilesStr = updatedFilesStr.slice(0, 100) + `...(${queue.length} files)`; } try { // we must add callback before update this._compiler.onUpdateFinish(async () => { // if there are more updates, recompile again if (this._updateQueue.length > 0) { await this.recompileAndSendResult(); } if (this._devServer?.config?.writeToDisk) { this._compiler.writeResourcesToDisk(); } }); checkClearScreen(this._compiler.config.config); const start = Date.now(); const result = await this._compiler.update(queue); this._logger.info(`${bold(cyan(updatedFilesStr))} updated in ${bold(green(`${Date.now() - start}ms`))}`); // clear update queue after update finished this._updateQueue = this._updateQueue.filter((item) => !queue.includes(item)); const { dynamicResources, dynamicModuleResourcesMap } = getDynamicResources(result.dynamicResourcesMap); const { added, changed, removed, immutableModules, mutableModules, boundaries } = result; const resultStr = `{ added: [${formatHmrResult(added)}], changed: [${formatHmrResult(changed)}], removed: [${formatHmrResult(removed)}], immutableModules: ${JSON.stringify(immutableModules.trim())}, mutableModules: ${JSON.stringify(mutableModules.trim())}, boundaries: ${JSON.stringify(boundaries)}, dynamicResources: ${JSON.stringify(dynamicResources)}, dynamicModuleResourcesMap: ${JSON.stringify(dynamicModuleResourcesMap)} }`; this.callUpdates(result); this._devServer.ws.clients.forEach((client) => { client.rawSend(` { type: 'farm-update', result: ${resultStr} } `); }); } catch (err) { checkClearScreen(this._compiler.config.config); throw new Error(logError(err)); } }; this._compiler = compiler; this._devServer = devServer; // this._lastAttemptWasError = false; this._lastModifiedTimestamp = new Map(); } callUpdates(result) { this._onUpdates?.forEach((cb) => cb(result)); } onUpdateFinish(cb) { if (!this._onUpdates) { this._onUpdates = []; } this._onUpdates.push(cb); } async hmrUpdate(absPath, force = false) { const paths = Array.isArray(absPath) ? absPath : [absPath]; for (const path of paths) { if (this._compiler.hasModule(path) && !this._updateQueue.includes(path)) { if (fse.existsSync(path)) { const lastModifiedTimestamp = this._lastModifiedTimestamp.get(path); const currentTimestamp = (await stat(path)).mtime.toISOString(); // only update the file if the timestamp changed since last update if (!force && lastModifiedTimestamp === currentTimestamp) { continue; } this._lastModifiedTimestamp.set(path, currentTimestamp); } // push the path into the queue this._updateQueue.push(path); } } if (!this._compiler.compiling && this._updateQueue.length > 0) { try { await this.recompileAndSendResult(); } catch (e) { // eslint-disable-next-line no-control-regex const serialization = e.message.replace(/\x1b\[[0-9;]*m/g, ''); const errorStr = `${JSON.stringify({ message: serialization })}`; this._devServer.ws.clients.forEach((client) => { client.rawSend(` { type: 'error', err: ${errorStr}, overlay: ${this._devServer.config.hmr.overlay} } `); }); this._logger.error(e); } } } } function formatHmrResult(array) { return array.map((item) => `'${item.replaceAll('\\', '\\\\')}'`).join(', '); } //# sourceMappingURL=hmr-engine.js.map