(mode = "read")
| 10 | * @error {NotAllowedError} If permission is not granted. |
| 11 | */ |
| 12 | export const openDirectory = async (mode = "read") => { |
| 13 | // Feature detection. The API needs to be supported |
| 14 | // and the app not run in an iframe. |
| 15 | const supportsFileSystemAccess = |
| 16 | "showDirectoryPicker" in window && |
| 17 | (() => { |
| 18 | try { |
| 19 | return window.self === window.top; |
| 20 | } catch { |
| 21 | return false; |
| 22 | } |
| 23 | })(); |
| 24 | // If the File System Access API is supported… |
| 25 | if (supportsFileSystemAccess) { |
| 26 | let directoryStructure = undefined; |
| 27 | |
| 28 | // Recursive function that walks the directory structure. |
| 29 | const getFiles = async (dirHandle, path = dirHandle.name) => { |
| 30 | const dirs = []; |
| 31 | const files = []; |
| 32 | for await (const entry of dirHandle.values()) { |
| 33 | const nestedPath = `${path}/${entry.name}`; |
| 34 | if (entry.kind === "file") { |
| 35 | files.push( |
| 36 | entry.getFile().then((file) => { |
| 37 | file.directoryHandle = dirHandle; |
| 38 | file.handle = entry; |
| 39 | return Object.defineProperty(file, "webkitRelativePath", { |
| 40 | configurable: true, |
| 41 | enumerable: true, |
| 42 | get: () => nestedPath, |
| 43 | }); |
| 44 | }) |
| 45 | ); |
| 46 | } else if (entry.kind === "directory") { |
| 47 | dirs.push(getFiles(entry, nestedPath)); |
| 48 | } |
| 49 | } |
| 50 | return [ |
| 51 | ...(await Promise.all(dirs)).flat(), |
| 52 | ...(await Promise.all(files)), |
| 53 | ]; |
| 54 | }; |
| 55 | |
| 56 | try { |
| 57 | // Open the directory. |
| 58 | const handle = await showDirectoryPicker({ |
| 59 | mode: mode |
| 60 | }); |
| 61 | // Get the directory structure. |
| 62 | directoryStructure = await getFiles(handle, undefined); |
| 63 | } catch (err) { |
| 64 | if (err.name !== "AbortError") { |
| 65 | console.error(err.name, err.message); |
| 66 | } |
| 67 | } |
| 68 | return Promise.resolve(directoryStructure); |
| 69 | } |
no test coverage detected