UNPKG

@theia/filesystem

Version:
323 lines • 14 kB
"use strict"; // ***************************************************************************** // Copyright (C) 2024 EclipseSource and others. // // This program and the accompanying materials are made available under the // terms of the Eclipse Public License v. 2.0 which is available at // http://www.eclipse.org/legal/epl-2.0. // // This Source Code may also be made available under the following Secondary // Licenses when the conditions for such availability set forth in the Eclipse // Public License v. 2.0 are satisfied: GNU General Public License, version 2 // with the GNU Classpath Exception which is available at // https://www.gnu.org/software/classpath/license.html. // // SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0 // ***************************************************************************** Object.defineProperty(exports, "__esModule", { value: true }); exports.OPFSFileSystemProvider = void 0; const tslib_1 = require("tslib"); const inversify_1 = require("@theia/core/shared/inversify"); const files_1 = require("../common/files"); const core_1 = require("@theia/core"); const opfs_filesystem_initialization_1 = require("./opfs-filesystem-initialization"); let OPFSFileSystemProvider = class OPFSFileSystemProvider { constructor() { this.capabilities = 2 /* FileSystemProviderCapabilities.FileReadWrite */; this.onDidChangeCapabilities = core_1.Event.None; this.onDidChangeFileEmitter = new core_1.Emitter(); this.onDidChangeFile = this.onDidChangeFileEmitter.event; this.onFileWatchError = core_1.Event.None; } init() { const setup = async () => { this.directoryHandle = await this.initialization.getRootDirectory(); await this.initialization.initializeFS(new Proxy(this, { get(target, prop, receiver) { if (prop === 'initialized') { return Promise.resolve(true); } return Reflect.get(target, prop, receiver); } })); return true; }; this.initialized = setup(); } watch(_resource, _opts) { return core_1.Disposable.NULL; } async exists(resource) { try { await this.initialized; await this.toFileSystemHandle(resource); return true; } catch (error) { return false; } } async stat(resource) { try { await this.initialized; const handle = await this.toFileSystemHandle(resource); if (handle.kind === 'file') { const fileHandle = handle; const file = await fileHandle.getFile(); return { type: files_1.FileType.File, ctime: file.lastModified, mtime: file.lastModified, size: file.size }; } else if (handle.kind === 'directory') { return { type: files_1.FileType.Directory, ctime: 0, mtime: 0, size: 0 }; } throw (0, files_1.createFileSystemProviderError)('Unknown file handle error', files_1.FileSystemProviderErrorCode.Unknown); } catch (error) { throw toFileSystemProviderError(error); } } async mkdir(resource) { await this.initialized; try { await this.toFileSystemHandle(resource, { create: true, isDirectory: true }); this.onDidChangeFileEmitter.fire([{ resource, type: 1 /* FileChangeType.ADDED */ }]); } catch (error) { throw toFileSystemProviderError(error, true); } } async readdir(resource) { await this.initialized; try { // Get the directory handle from the directoryHandle const directoryHandle = await this.toFileSystemHandle(resource, { create: false, isDirectory: true }); const result = []; // Iterate through the entries in the directory (files and subdirectories) for await (const [name, handle] of directoryHandle.entries()) { // Determine the type of the entry (file or directory) if (handle.kind === 'file') { result.push([name, files_1.FileType.File]); } else if (handle.kind === 'directory') { result.push([name, files_1.FileType.Directory]); } } return result; } catch (error) { throw toFileSystemProviderError(error, true); } } async delete(resource, _opts) { await this.initialized; try { const parentURI = resource.parent; const parentHandle = await this.toFileSystemHandle(parentURI, { create: false, isDirectory: true }); if (parentHandle.kind !== 'directory') { throw (0, files_1.createFileSystemProviderError)(new Error('Parent is not a directory'), files_1.FileSystemProviderErrorCode.FileNotADirectory); } const name = resource.path.base; return parentHandle.removeEntry(name, { recursive: _opts.recursive }); } catch (error) { throw toFileSystemProviderError(error); } finally { this.onDidChangeFileEmitter.fire([{ resource, type: 2 /* FileChangeType.DELETED */ }]); } } async rename(from, to, opts) { await this.initialized; try { const fromHandle = await this.toFileSystemHandle(from); // Check whether the source is a file or directory if (fromHandle.kind === 'directory') { // Create the new directory and get the handle await this.mkdir(to); const toHandle = await this.toFileSystemHandle(to); await copyDirectoryContents(fromHandle, toHandle); // Delete the old directory await this.delete(from, { recursive: true, useTrash: false }); } else { const content = await this.readFile(from); await this.writeFile(to, content, { create: true, overwrite: opts.overwrite }); await this.delete(from, { recursive: true, useTrash: false }); } this.onDidChangeFileEmitter.fire([{ resource: to, type: 1 /* FileChangeType.ADDED */ }]); } catch (error) { throw toFileSystemProviderError(error); } } async readFile(resource) { await this.initialized; try { // Get the file handle from the directoryHandle const fileHandle = await this.toFileSystemHandle(resource, { create: false, isDirectory: false }); // Get the file itself (which includes the content) const file = await fileHandle.getFile(); // Read the file as an ArrayBuffer and convert it to Uint8Array const arrayBuffer = await file.arrayBuffer(); return new Uint8Array(arrayBuffer); } catch (error) { throw toFileSystemProviderError(error, false); } } async writeFile(resource, content, opts) { await this.initialized; let writeableHandle = undefined; try { // Validate target unless { create: true, overwrite: true } if (!opts.create || !opts.overwrite) { const fileExists = await this.stat(resource).then(() => true, () => false); if (fileExists) { if (!opts.overwrite) { throw (0, files_1.createFileSystemProviderError)('File already exists', files_1.FileSystemProviderErrorCode.FileExists); } } else { if (!opts.create) { throw (0, files_1.createFileSystemProviderError)('File does not exist', files_1.FileSystemProviderErrorCode.FileNotFound); } } } const handle = await this.toFileSystemHandle(resource, { create: true, isDirectory: false }); // Open writeableHandle = await (handle === null || handle === void 0 ? void 0 : handle.createWritable()); // Write content at once await (writeableHandle === null || writeableHandle === void 0 ? void 0 : writeableHandle.write(content)); this.onDidChangeFileEmitter.fire([{ resource: resource, type: 0 /* FileChangeType.UPDATED */ }]); } catch (error) { throw toFileSystemProviderError(error, false); } finally { if (typeof writeableHandle !== 'undefined') { await writeableHandle.close(); } } } /** * Returns the FileSystemHandle for the given resource given by a URI. * @param resource URI/path of the resource * @param options Options for the creation of the handle while traversing the path * @returns FileSystemHandle for the given resource */ async toFileSystemHandle(resource, options) { const pathParts = resource.path.toString().split(core_1.Path.separator).filter(Boolean); return recursiveFileSystemHandle(this.directoryHandle, pathParts, options); } }; exports.OPFSFileSystemProvider = OPFSFileSystemProvider; tslib_1.__decorate([ (0, inversify_1.inject)(opfs_filesystem_initialization_1.OPFSInitialization), tslib_1.__metadata("design:type", Object) ], OPFSFileSystemProvider.prototype, "initialization", void 0); tslib_1.__decorate([ (0, inversify_1.postConstruct)(), tslib_1.__metadata("design:type", Function), tslib_1.__metadata("design:paramtypes", []), tslib_1.__metadata("design:returntype", void 0) ], OPFSFileSystemProvider.prototype, "init", null); exports.OPFSFileSystemProvider = OPFSFileSystemProvider = tslib_1.__decorate([ (0, inversify_1.injectable)() ], OPFSFileSystemProvider); // #region Helper functions async function recursiveFileSystemHandle(handle, pathParts, options) { // We reached the end of the path, this happens only when not creating if (pathParts.length === 0) { return handle; } // If there are parts left, the handle must be a directory if (handle.kind !== 'directory') { throw (0, files_1.createFileSystemProviderError)('Not a directory', files_1.FileSystemProviderErrorCode.FileNotADirectory); } const dirHandle = handle; // We need to create it and thus we need to stop early to create the file or directory if (pathParts.length === 1 && (options === null || options === void 0 ? void 0 : options.create)) { if (options === null || options === void 0 ? void 0 : options.isDirectory) { return dirHandle.getDirectoryHandle(pathParts[0], { create: options.create }); } else { return dirHandle.getFileHandle(pathParts[0], { create: options.create }); } } // Continue to resolve the path const part = pathParts.shift(); for await (const entry of dirHandle.entries()) { // Check the entry name in the current directory if (entry[0] === part) { return recursiveFileSystemHandle(entry[1], pathParts, options); } } // If we haven't found the part, we need to create it along the way if (options === null || options === void 0 ? void 0 : options.create) { const newHandle = await dirHandle.getDirectoryHandle(part, { create: true }); return recursiveFileSystemHandle(newHandle, pathParts, options); } throw (0, files_1.createFileSystemProviderError)('File not found', files_1.FileSystemProviderErrorCode.FileNotFound); } // Function to copy directory contents recursively async function copyDirectoryContents(sourceHandle, destinationHandle) { for await (const [name, handle] of sourceHandle.entries()) { if (handle.kind === 'file') { const file = await handle.getFile(); const newFileHandle = await destinationHandle.getFileHandle(name, { create: true }); const writable = await newFileHandle.createWritable(); try { await writable.write(await file.arrayBuffer()); } finally { await writable.close(); } } else if (handle.kind === 'directory') { const newSubDirHandle = await destinationHandle.getDirectoryHandle(name, { create: true }); await copyDirectoryContents(handle, newSubDirHandle); } } } function toFileSystemProviderError(error, is_dir) { if (error instanceof files_1.FileSystemProviderError) { return error; // avoid double conversion } let code; switch (error.name) { case 'NotFoundError': code = files_1.FileSystemProviderErrorCode.FileNotFound; break; case 'InvalidModificationError': code = files_1.FileSystemProviderErrorCode.FileExists; break; case 'NotAllowedError': code = files_1.FileSystemProviderErrorCode.NoPermissions; break; case 'TypeMismatchError': if (!is_dir) { code = files_1.FileSystemProviderErrorCode.FileIsADirectory; } else { code = files_1.FileSystemProviderErrorCode.FileNotADirectory; } break; case 'QuotaExceededError': code = files_1.FileSystemProviderErrorCode.FileTooLarge; break; default: code = files_1.FileSystemProviderErrorCode.Unknown; } return (0, files_1.createFileSystemProviderError)(error, code); } // #endregion //# sourceMappingURL=opfs-filesystem-provider.js.map