UNPKG

@rushstack/heft

Version:

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

245 lines 10.7 kB
"use strict"; // Copyright (c) Microsoft Corporation. All rights reserved. Licensed under the MIT license. // See LICENSE in the project root for license information. var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || (function () { var ownKeys = function(o) { ownKeys = Object.getOwnPropertyNames || function (o) { var ar = []; for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; return ar; }; return ownKeys(o); }; return function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); __setModuleDefault(result, mod); return result; }; })(); Object.defineProperty(exports, "__esModule", { value: true }); exports.StaticFileSystemAdapter = void 0; const path = __importStar(require("path")); const node_core_library_1 = require("@rushstack/node-core-library"); /* eslint-enable @rushstack/no-new-null */ const IS_WINDOWS = process.platform === 'win32'; /** * A filesystem adapter for use with the "fast-glob" package. This adapter uses a static set of paths * to provide a virtual filesystem. * * @remarks This adapter only implements methods required to allow for globbing. This means that it * does not support returning true-to-disk file stats or dirent objects. Instead, the returned file * stats and dirent objects only implement the `isDirectory` and `isFile` methods, which are * required for filesystem traversal performed by the globber. */ class StaticFileSystemAdapter { /** * Create a new StaticFileSystemAdapter instance with the provided file paths. */ constructor(filePaths) { this._directoryMap = new Map(); /** { @inheritdoc fs.lstat } */ this.lstat = ((filePath, callback) => { process.nextTick(() => { let result; try { result = this.lstatSync(filePath); } catch (e) { callback(e, {}); return; } callback(null, result); }); }); /** { @inheritdoc fs.lstatSync } */ this.lstatSync = ((filePath) => { filePath = this._normalizePath(filePath); const entry = this._directoryMap.get(filePath); if (!entry) { const error = new Error(`ENOENT: no such file or directory, stat '${filePath}'`); error.code = 'ENOENT'; error.syscall = 'stat'; error.errno = -4058; error.path = filePath; throw error; } // We should only need to implement these methods for the purposes of fast-glob return { isFile: () => !entry.children, isDirectory: () => !!entry.children, isBlockDevice: () => false, isCharacterDevice: () => false, isSymbolicLink: () => false, isFIFO: () => false, isSocket: () => false }; }); /** { @inheritdoc fs.stat } */ this.stat = ((filePath, callback) => { this.lstat(filePath, callback); }); /** { @inheritdoc fs.statSync } */ this.statSync = ((filePath) => { return this.lstatSync(filePath); }); /** { @inheritdoc fs.readdir } */ this.readdir = ((filePath, optionsOrCallback, callback) => { // 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; } // Perform the readdir on the next tick to avoid blocking the event loop process.nextTick(() => { let result; try { if (options === null || options === void 0 ? void 0 : options.withFileTypes) { result = this.readdirSync(filePath, options); } else { result = this.readdirSync(filePath); } } catch (e) { callback(e, []); return; } // When "withFileTypes" is false or undefined, the callback is expected to return a string array. // Otherwise, we return a fs.Dirent array. if (options === null || options === void 0 ? void 0 : options.withFileTypes) { callback(null, result); } else { callback(null, result); } }); }); /** { @inheritdoc fs.readdirSync } */ this.readdirSync = ((filePath, options) => { filePath = this._normalizePath(filePath); const virtualDirectory = this._directoryMap.get(filePath); if (!virtualDirectory) { // Immitate a missing directory read from fs.readdir const error = new Error(`ENOENT: no such file or directory, scandir '${filePath}'`); error.code = 'ENOENT'; error.syscall = 'scandir'; error.errno = -4058; error.path = filePath; throw error; } else if (!virtualDirectory.children) { // Immitate a directory read of a file from fs.readdir const error = new Error(`ENOTDIR: not a directory, scandir '${filePath}'`); error.code = 'ENOTDIR'; error.syscall = 'scandir'; error.errno = -4052; error.path = filePath; throw error; } // When "withFileTypes" is false or undefined, the method is expected to return a string array. // Otherwise, we return a fs.Dirent array. const result = Array.from(virtualDirectory.children); if (options === null || options === void 0 ? void 0 : options.withFileTypes) { return result.map((entry) => { // Partially implement the fs.Dirent interface, only including the properties used by fast-glob return { name: entry.name, isFile: () => !entry.children, isDirectory: () => !!entry.children, isBlockDevice: () => false, isCharacterDevice: () => false, isSymbolicLink: () => false, isFIFO: () => false, isSocket: () => false }; }); } else { return result.map((entry) => entry.name); } }); for (const filePath of filePaths || []) { this.addFile(filePath); } } /** * Add a file and it's parent directories to the static virtual filesystem. */ addFile(filePath) { filePath = this._normalizePath(filePath); const existingPath = this._directoryMap.get(filePath); if (!existingPath) { // Set an entry without children for the file. Entries with undefined children are assumed to be files. let childPath = filePath; let childEntry = { name: path.basename(childPath) }; this._directoryMap.set(childPath, childEntry); // Loop through the path segments and create entries for each directory, if they don't already exist. // If they do, append to the existing children set and continue. let parentPath; while ((parentPath = path.dirname(childPath)) !== childPath) { const existingParentEntry = this._directoryMap.get(parentPath); if (existingParentEntry) { // If there is already an existing parent entry, add the child entry to the existing children set // and exit early, since the parent entries already exist. existingParentEntry.children.add(childEntry); break; } else { // If there is no existing parent entry, create a new entry with the child entry as the only child. const parentEntry = { name: path.basename(parentPath), children: new Set([childEntry]) }; this._directoryMap.set(parentPath, parentEntry); childEntry = parentEntry; childPath = parentPath; } } } } /** * Remove a file from the static virtual filesystem. */ removeFile(filePath) { filePath = this._normalizePath(filePath); const existingEntry = this._directoryMap.get(filePath); if (existingEntry) { // Remove the entry from the map and the parent's children set this._directoryMap.delete(filePath); this._directoryMap.get(path.dirname(filePath)).children.delete(existingEntry); } } /** * Remove all files from the static virtual filesystem. */ removeAllFiles() { this._directoryMap.clear(); } _normalizePath(filePath) { // On Windows, normalize to backslashes so that errors have the correct path format return IS_WINDOWS ? node_core_library_1.Path.convertToBackslashes(filePath) : filePath; } } exports.StaticFileSystemAdapter = StaticFileSystemAdapter; //# sourceMappingURL=StaticFileSystemAdapter.js.map