UNPKG

@angular-devkit/core

Version:

Angular DevKit - Core Utility Library

332 lines (331 loc) 11.1 kB
"use strict"; /** * @license * Copyright Google LLC All Rights Reserved. * * Use of this source code is governed by an MIT-style license that can be * found in the LICENSE file at https://angular.dev/license */ Object.defineProperty(exports, "__esModule", { value: true }); exports.SimpleMemoryHost = void 0; const rxjs_1 = require("rxjs"); const exception_1 = require("../../exception"); const path_1 = require("../path"); const interface_1 = require("./interface"); class SimpleMemoryHost { _cache = new Map(); _watchers = new Map(); _newDirStats() { return { inspect() { return '<Directory>'; }, isFile() { return false; }, isDirectory() { return true; }, size: 0, atime: new Date(), ctime: new Date(), mtime: new Date(), birthtime: new Date(), content: null, }; } _newFileStats(content, oldStats) { return { inspect() { return `<File size(${content.byteLength})>`; }, isFile() { return true; }, isDirectory() { return false; }, size: content.byteLength, atime: oldStats ? oldStats.atime : new Date(), ctime: new Date(), mtime: new Date(), birthtime: oldStats ? oldStats.birthtime : new Date(), content, }; } constructor() { this._cache.set((0, path_1.normalize)('/'), this._newDirStats()); } _toAbsolute(path) { return (0, path_1.isAbsolute)(path) ? path : (0, path_1.normalize)('/' + path); } _updateWatchers(path, type) { const time = new Date(); let currentPath = path; let parent = null; if (this._watchers.size == 0) { // Nothing to do if there's no watchers. return; } const maybeWatcher = this._watchers.get(currentPath); if (maybeWatcher) { maybeWatcher.forEach((watcher) => { const [options, subject] = watcher; subject.next({ path, time, type }); if (!options.persistent && type == interface_1.HostWatchEventType.Deleted) { subject.complete(); this._watchers.delete(currentPath); } }); } do { currentPath = parent !== null ? parent : currentPath; parent = (0, path_1.dirname)(currentPath); const maybeWatcher = this._watchers.get(currentPath); if (maybeWatcher) { maybeWatcher.forEach((watcher) => { const [options, subject] = watcher; if (!options.recursive) { return; } subject.next({ path, time, type }); if (!options.persistent && type == interface_1.HostWatchEventType.Deleted) { subject.complete(); this._watchers.delete(currentPath); } }); } } while (parent != currentPath); } get capabilities() { return { synchronous: true }; } /** * List of protected methods that give direct access outside the observables to the cache * and internal states. */ _write(path, content) { path = this._toAbsolute(path); const old = this._cache.get(path); if (old && old.isDirectory()) { throw new exception_1.PathIsDirectoryException(path); } // Update all directories. If we find a file we know it's an invalid write. const fragments = (0, path_1.split)(path); let curr = (0, path_1.normalize)('/'); for (const fr of fragments) { curr = (0, path_1.join)(curr, fr); const maybeStats = this._cache.get(fr); if (maybeStats) { if (maybeStats.isFile()) { throw new exception_1.PathIsFileException(curr); } } else { this._cache.set(curr, this._newDirStats()); } } // Create the stats. const stats = this._newFileStats(content, old); this._cache.set(path, stats); this._updateWatchers(path, old ? interface_1.HostWatchEventType.Changed : interface_1.HostWatchEventType.Created); } _read(path) { path = this._toAbsolute(path); const maybeStats = this._cache.get(path); if (!maybeStats) { throw new exception_1.FileDoesNotExistException(path); } else if (maybeStats.isDirectory()) { throw new exception_1.PathIsDirectoryException(path); } else if (!maybeStats.content) { throw new exception_1.PathIsDirectoryException(path); } else { return maybeStats.content; } } _delete(path) { path = this._toAbsolute(path); if (this._isDirectory(path)) { for (const [cachePath] of this._cache.entries()) { if (cachePath.startsWith(path + path_1.NormalizedSep) || cachePath === path) { this._cache.delete(cachePath); } } } else { this._cache.delete(path); } this._updateWatchers(path, interface_1.HostWatchEventType.Deleted); } _rename(from, to) { from = this._toAbsolute(from); to = this._toAbsolute(to); if (!this._cache.has(from)) { throw new exception_1.FileDoesNotExistException(from); } else if (this._cache.has(to)) { throw new exception_1.FileAlreadyExistException(to); } if (this._isDirectory(from)) { for (const path of this._cache.keys()) { if (path.startsWith(from + path_1.NormalizedSep)) { const content = this._cache.get(path); if (content) { // We don't need to clone or extract the content, since we're moving files. this._cache.set((0, path_1.join)(to, path_1.NormalizedSep, path.slice(from.length)), content); } } } } else { const content = this._cache.get(from); if (content) { const fragments = (0, path_1.split)(to); const newDirectories = []; let curr = (0, path_1.normalize)('/'); for (const fr of fragments) { curr = (0, path_1.join)(curr, fr); const maybeStats = this._cache.get(fr); if (maybeStats) { if (maybeStats.isFile()) { throw new exception_1.PathIsFileException(curr); } } else { newDirectories.push(curr); } } for (const newDirectory of newDirectories) { this._cache.set(newDirectory, this._newDirStats()); } this._cache.delete(from); this._cache.set(to, content); } } this._updateWatchers(from, interface_1.HostWatchEventType.Renamed); } _list(path) { path = this._toAbsolute(path); if (this._isFile(path)) { throw new exception_1.PathIsFileException(path); } const fragments = (0, path_1.split)(path); const result = new Set(); if (path !== path_1.NormalizedRoot) { for (const p of this._cache.keys()) { if (p.startsWith(path + path_1.NormalizedSep)) { result.add((0, path_1.split)(p)[fragments.length]); } } } else { for (const p of this._cache.keys()) { if (p.startsWith(path_1.NormalizedSep) && p !== path_1.NormalizedRoot) { result.add((0, path_1.split)(p)[1]); } } } return [...result]; } _exists(path) { return !!this._cache.get(this._toAbsolute(path)); } _isDirectory(path) { const maybeStats = this._cache.get(this._toAbsolute(path)); return maybeStats ? maybeStats.isDirectory() : false; } _isFile(path) { const maybeStats = this._cache.get(this._toAbsolute(path)); return maybeStats ? maybeStats.isFile() : false; } _stat(path) { const maybeStats = this._cache.get(this._toAbsolute(path)); if (!maybeStats) { return null; } else { return maybeStats; } } _watch(path, options) { path = this._toAbsolute(path); const subject = new rxjs_1.Subject(); let maybeWatcherArray = this._watchers.get(path); if (!maybeWatcherArray) { maybeWatcherArray = []; this._watchers.set(path, maybeWatcherArray); } maybeWatcherArray.push([options || {}, subject]); return subject.asObservable(); } write(path, content) { return new rxjs_1.Observable((obs) => { this._write(path, content); obs.next(); obs.complete(); }); } read(path) { return new rxjs_1.Observable((obs) => { const content = this._read(path); obs.next(content); obs.complete(); }); } delete(path) { return new rxjs_1.Observable((obs) => { this._delete(path); obs.next(); obs.complete(); }); } rename(from, to) { return new rxjs_1.Observable((obs) => { this._rename(from, to); obs.next(); obs.complete(); }); } list(path) { return new rxjs_1.Observable((obs) => { obs.next(this._list(path)); obs.complete(); }); } exists(path) { return new rxjs_1.Observable((obs) => { obs.next(this._exists(path)); obs.complete(); }); } isDirectory(path) { return new rxjs_1.Observable((obs) => { obs.next(this._isDirectory(path)); obs.complete(); }); } isFile(path) { return new rxjs_1.Observable((obs) => { obs.next(this._isFile(path)); obs.complete(); }); } // Some hosts may not support stat. stat(path) { return new rxjs_1.Observable((obs) => { obs.next(this._stat(path)); obs.complete(); }); } watch(path, options) { return this._watch(path, options); } reset() { this._cache.clear(); this._watchers.clear(); } } exports.SimpleMemoryHost = SimpleMemoryHost;