UNPKG

@rushstack/heft

Version:

Build all your JavaScript projects the same way: A way that works.

219 lines 8.37 kB
// Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. import * as fs from 'node:fs'; import * as path from 'node:path'; import Watchpack from 'watchpack'; /** * A filesystem adapter for use with the "fast-glob" package. This adapter tracks file system accesses * to initialize `watchpack`. */ export class WatchFileSystemAdapter { constructor() { this._files = new Map(); this._contexts = new Map(); this._missing = new Map(); /** { @inheritdoc fs.readdirSync } */ this.readdirSync = ((filePath, options) => { filePath = path.normalize(filePath); try { if (options === null || options === void 0 ? void 0 : options.withFileTypes) { const results = fs.readdirSync(filePath, options); this._contexts.set(filePath, Date.now()); return results; } else { const results = fs.readdirSync(filePath); this._contexts.set(filePath, Date.now()); return results; } } catch (err) { this._missing.set(filePath, Date.now()); throw err; } }); /** { @inheritdoc fs.readdir } */ this.readdir = (filePath, optionsOrCallback, callback) => { filePath = path.normalize(filePath); // Default to no options, which will return a string callback let options; if (typeof optionsOrCallback === 'object') { options = optionsOrCallback; } else if (typeof optionsOrCallback === 'function') { callback = optionsOrCallback; } if (options === null || options === void 0 ? void 0 : options.withFileTypes) { fs.readdir(filePath, options, (err, entries) => { if (err) { this._missing.set(filePath, Date.now()); } else { this._contexts.set(filePath, Date.now()); } callback(err, entries); }); } else { fs.readdir(filePath, (err, entries) => { if (err) { this._missing.set(filePath, Date.now()); } else { this._contexts.set(filePath, Date.now()); } callback(err, entries); }); } }; /** { @inheritdoc fs.lstat } */ this.lstat = (filePath, callback) => { filePath = path.normalize(filePath); fs.lstat(filePath, (err, stats) => { if (err) { this._missing.set(filePath, Date.now()); } else { this._files.set(filePath, stats.mtime.getTime() || stats.ctime.getTime() || Date.now()); } callback(err, stats); }); }; /** { @inheritdoc fs.lstatSync } */ this.lstatSync = (filePath) => { filePath = path.normalize(filePath); try { const stats = fs.lstatSync(filePath); this._files.set(filePath, stats.mtime.getTime() || stats.ctime.getTime() || Date.now()); return stats; } catch (err) { this._missing.set(filePath, Date.now()); throw err; } }; /** { @inheritdoc fs.stat } */ this.stat = (filePath, callback) => { filePath = path.normalize(filePath); fs.stat(filePath, (err, stats) => { if (err) { this._missing.set(filePath, Date.now()); } else { this._files.set(filePath, stats.mtime.getTime() || stats.ctime.getTime() || Date.now()); } callback(err, stats); }); }; /** { @inheritdoc fs.statSync } */ this.statSync = (filePath) => { filePath = path.normalize(filePath); try { const stats = fs.statSync(filePath); this._files.set(filePath, stats.mtime.getTime() || stats.ctime.getTime() || Date.now()); return stats; } catch (err) { this._missing.set(filePath, Date.now()); throw err; } }; } /** * @inheritdoc */ setBaseline() { this._lastQueryTime = Date.now(); if (this._watcher) { const times = new Map(); this._watcher.pause(); this._watcher.collectTimeInfoEntries(times, times); this._times = times; } } /** * @inheritdoc */ watch(onChange) { if (this._files.size === 0 && this._contexts.size === 0 && this._missing.size === 0) { return; } const watcher = new Watchpack({ aggregateTimeout: 0, followSymlinks: false }); this._watcher = watcher; watcher.watch({ files: this._files.keys(), directories: this._contexts.keys(), missing: this._missing.keys(), startTime: this._lastQueryTime }); this._lastFiles = this._files; this._files = new Map(); this._contexts.clear(); this._missing.clear(); watcher.once('aggregated', onChange); } /** * @inheritdoc */ async getStateAndTrackAsync(filePath) { var _a, _b, _c; const normalizedSourcePath = path.normalize(filePath); const oldTime = (_a = this._lastFiles) === null || _a === void 0 ? void 0 : _a.get(normalizedSourcePath); let newTimeEntry = (_b = this._times) === null || _b === void 0 ? void 0 : _b.get(normalizedSourcePath); if (!newTimeEntry) { // Need to record a timestamp, otherwise first rerun will select everything try { const stats = await fs.promises.lstat(normalizedSourcePath); const rounded = stats.mtime.getTime() || stats.ctime.getTime() || Date.now(); newTimeEntry = { timestamp: rounded, safeTime: rounded }; } catch (err) { this._missing.set(normalizedSourcePath, Date.now()); } } const newTime = (newTimeEntry && ((_c = newTimeEntry.timestamp) !== null && _c !== void 0 ? _c : newTimeEntry.safeTime)) || this._lastQueryTime; if (newTime) { this._files.set(normalizedSourcePath, newTime); } return { changed: newTime !== oldTime }; } /** * @inheritdoc */ getStateAndTrack(filePath) { var _a, _b, _c; const normalizedSourcePath = path.normalize(filePath); const oldTime = (_a = this._lastFiles) === null || _a === void 0 ? void 0 : _a.get(normalizedSourcePath); let newTimeEntry = (_b = this._times) === null || _b === void 0 ? void 0 : _b.get(normalizedSourcePath); if (!newTimeEntry) { // Need to record a timestamp, otherwise first rerun will select everything const stats = fs.lstatSync(normalizedSourcePath, { throwIfNoEntry: false }); if (stats) { const rounded = stats.mtime.getTime() || stats.ctime.getTime() || Date.now(); newTimeEntry = { timestamp: rounded, safeTime: rounded }; } else { this._missing.set(normalizedSourcePath, Date.now()); } } const newTime = (newTimeEntry && ((_c = newTimeEntry.timestamp) !== null && _c !== void 0 ? _c : newTimeEntry.safeTime)) || this._lastQueryTime; if (newTime) { this._files.set(normalizedSourcePath, newTime); } return { changed: newTime !== oldTime }; } } //# sourceMappingURL=WatchFileSystemAdapter.js.map