1- import { getPathComponents } from "./path.ts" ;
1+ import type {
2+ RootedDirectoryPath ,
3+ RootedFilePath ,
4+ RootedPath ,
5+ } from "../ast/index.ts" ;
6+ import {
7+ getPathComponents ,
8+ toRootedFilePath ,
9+ } from "./path.ts" ;
210
311export interface FileSystemEntries {
412 files : string [ ] ;
513 directories : string [ ] ;
614}
715
816export interface FileSystem {
9- directoryExists ?: ( directoryName : string ) => boolean | undefined ;
10- fileExists ?: ( fileName : string ) => boolean | undefined ;
11- getAccessibleEntries ?: ( directoryName : string ) => FileSystemEntries | undefined ;
17+ directoryExists ?: ( directoryName : RootedDirectoryPath ) => boolean | undefined ;
18+ fileExists ?: ( fileName : RootedFilePath ) => boolean | undefined ;
19+ getAccessibleEntries ?: ( directoryName : RootedDirectoryPath ) => FileSystemEntries | undefined ;
1220 /**
1321 * Read a file's content.
1422 * - Return the file content as a `string` (including `""` for empty files).
1523 * - Return `null` to indicate the file does not exist (without falling back to the real FS).
1624 * - Return `undefined` to fall back to the real filesystem.
1725 */
18- readFile ?: ( fileName : string ) => string | null | undefined ;
19- realpath ?: ( path : string ) => string | undefined ;
20- writeFile ?: ( path : string , content : string ) => void ;
21- removeFile ?: ( path : string ) => void ;
26+ readFile ?: ( fileName : RootedFilePath ) => string | null | undefined ;
27+ realpath ?: ( path : RootedPath ) => RootedPath | undefined ;
28+ writeFile ?: ( path : RootedFilePath , content : string ) => void ;
29+ removeFile ?: ( path : RootedFilePath ) => void ;
30+ }
31+
32+ export interface VirtualFileSystem extends FileSystem {
33+ directoryExists ( directoryName : RootedDirectoryPath ) : boolean ;
34+ fileExists ( fileName : RootedFilePath ) : boolean ;
35+ getAccessibleEntries ( directoryName : RootedDirectoryPath ) : FileSystemEntries | undefined ;
36+ readFile ( fileName : RootedFilePath ) : string | undefined ;
37+ realpath ( path : RootedPath ) : RootedPath ;
38+ writeFile ( path : RootedFilePath , content : string ) : void ;
39+ removeFile ( path : RootedFilePath ) : void ;
2240}
2341
2442/** The callback names supported by the Go server for virtual FS delegation. */
@@ -35,15 +53,16 @@ interface VFile {
3553
3654type VNode = VDirectory | VFile ;
3755
38- export function createVirtualFileSystem ( files : Record < string , string > ) : FileSystem {
56+ export function createVirtualFileSystem ( files : Record < string , string > ) : VirtualFileSystem {
3957 const root : VDirectory = {
4058 type : "directory" ,
4159 children : { } ,
4260 } ;
43- const content : Record < string , string > = { } ;
61+ const content = new Map < RootedFilePath , string > ( ) ;
4462
45- for ( const filePath of Object . keys ( files ) ) {
46- content [ filePath ] = files [ filePath ] ;
63+ for ( const [ rawFilePath , data ] of Object . entries ( files ) ) {
64+ const filePath = toRootedFilePath ( rawFilePath , undefined ) ;
65+ content . set ( filePath , data ) ;
4766 addToTree ( filePath ) ;
4867 }
4968
@@ -57,11 +76,14 @@ export function createVirtualFileSystem(files: Record<string, string>): FileSyst
5776 removeFile,
5877 } ;
5978
60- function getNodeFromPath ( path : string ) : VNode | undefined {
79+ function getNodeFromPath ( path : RootedPath ) : VNode | undefined {
6180 if ( ! path || path === "/" ) {
6281 return root ;
6382 }
64- const segments = getPathComponents ( path ) . slice ( 1 ) ;
83+ return getNodeFromSegments ( getPathComponents ( path ) . slice ( 1 ) ) ;
84+ }
85+
86+ function getNodeFromSegments ( segments : readonly string [ ] ) : VNode | undefined {
6587 let current : VNode = root ;
6688 for ( const segment of segments ) {
6789 if ( current . type !== "directory" ) {
@@ -90,7 +112,7 @@ export function createVirtualFileSystem(files: Record<string, string>): FileSyst
90112 return current ;
91113 }
92114
93- function addToTree ( path : string ) : void {
115+ function addToTree ( path : RootedFilePath ) : void {
94116 const segments = getPathComponents ( path ) . slice ( 1 ) ;
95117 if ( segments . length === 0 ) {
96118 throw new Error ( `Invalid file path: "${ path } "` ) ;
@@ -100,32 +122,32 @@ export function createVirtualFileSystem(files: Record<string, string>): FileSyst
100122 dirNode . children [ filename ] = { type : "file" } ;
101123 }
102124
103- function writeFile ( path : string , data : string ) : void {
104- content [ path ] = data ;
125+ function writeFile ( path : RootedFilePath , data : string ) : void {
126+ content . set ( path , data ) ;
105127 addToTree ( path ) ;
106128 }
107129
108- function removeFile ( path : string ) : void {
109- delete content [ path ] ;
130+ function removeFile ( path : RootedFilePath ) : void {
131+ content . delete ( path ) ;
110132 const segments = getPathComponents ( path ) . slice ( 1 ) ;
111133 if ( segments . length === 0 ) return ;
112134 const filename = segments . pop ( ) ! ;
113- const dirNode = getNodeFromPath ( "/" + segments . join ( "/" ) ) ;
135+ const dirNode = getNodeFromSegments ( segments ) ;
114136 if ( dirNode && dirNode . type === "directory" ) {
115137 delete dirNode . children [ filename ] ;
116138 }
117139 }
118140
119- function directoryExists ( directoryName : string ) : boolean {
141+ function directoryExists ( directoryName : RootedDirectoryPath ) : boolean {
120142 const node = getNodeFromPath ( directoryName ) ;
121143 return ! ! node && node . type === "directory" ;
122144 }
123145
124- function fileExists ( fileName : string ) : boolean {
125- return fileName in content ;
146+ function fileExists ( fileName : RootedFilePath ) : boolean {
147+ return content . has ( fileName ) ;
126148 }
127149
128- function getAccessibleEntries ( directoryName : string ) : FileSystemEntries | undefined {
150+ function getAccessibleEntries ( directoryName : RootedDirectoryPath ) : FileSystemEntries | undefined {
129151 const node = getNodeFromPath ( directoryName ) ;
130152 if ( ! node || node . type !== "directory" ) {
131153 return undefined ;
@@ -143,10 +165,7 @@ export function createVirtualFileSystem(files: Record<string, string>): FileSyst
143165 return { files : fileEntries , directories } ;
144166 }
145167
146- function readFile ( fileName : string ) : string | undefined {
147- if ( fileName in content ) {
148- return content [ fileName ] ;
149- }
150- return undefined ;
168+ function readFile ( fileName : RootedFilePath ) : string | undefined {
169+ return content . get ( fileName ) ;
151170 }
152171}
0 commit comments