11import { Contents , ServerConnection } from '@jupyterlab/services' ;
22
3+ import { PathExt } from '@jupyterlab/coreutils' ;
4+
35import { ISignal , Signal } from '@lumino/signaling' ;
46
7+ async function toArray < T > (
8+ asyncIterator : AsyncIterableIterator < T >
9+ ) : Promise < T [ ] > {
10+ const arr = [ ] ;
11+
12+ for await ( const i of asyncIterator ) {
13+ arr . push ( i ) ;
14+ }
15+
16+ return arr ;
17+ }
18+
519export class FileSystemDrive implements Contents . IDrive {
620 get isDisposed ( ) : boolean {
721 return this . _isDisposed ;
@@ -36,7 +50,7 @@ export class FileSystemDrive implements Contents.IDrive {
3650 }
3751
3852 async get (
39- localPath : string ,
53+ path : string ,
4054 options ?: Contents . IFetchOptions
4155 ) : Promise < Contents . IModel > {
4256 const root = this . _rootHandle ;
@@ -55,12 +69,33 @@ export class FileSystemDrive implements Contents.IDrive {
5569 } ;
5670 }
5771
72+ let parentHandle = root ;
73+ // If requesting a file/directory that is not under root, we need the right directory handle
74+ for ( const subPath of path . split ( '/' ) . slice ( 0 , - 1 ) ) {
75+ parentHandle = await parentHandle . getDirectoryHandle ( subPath ) ;
76+ }
77+
78+ const parentPath = PathExt . dirname ( path ) ;
79+ const localPath = PathExt . basename ( path ) ;
80+
81+ let localHandle : FileSystemDirectoryHandle | FileSystemFileHandle ;
82+
83+ const currentContent = await toArray ( parentHandle . values ( ) ) ;
84+
5885 if ( localPath ) {
59- const handle = await root . getFileHandle ( localPath ) ;
60- const file = await handle . getFile ( ) ;
86+ localHandle = currentContent . filter (
87+ element => element . name === localPath
88+ ) [ 0 ] ;
89+ } else {
90+ localHandle = parentHandle ;
91+ }
92+
93+ if ( localHandle . kind === 'file' ) {
94+ const file = await localHandle . getFile ( ) ;
95+
6196 return {
6297 name : file . name ,
63- path : file . name ,
98+ path : PathExt . join ( parentPath , localPath ) ,
6499 created : new Date ( file . lastModified ) . toISOString ( ) ,
65100 last_modified : new Date ( file . lastModified ) . toISOString ( ) ,
66101 format : 'text' ,
@@ -69,72 +104,71 @@ export class FileSystemDrive implements Contents.IDrive {
69104 type : 'file' ,
70105 mimetype : 'text/plain'
71106 } ;
72- }
73-
74- const content : Contents . IModel [ ] = [ ] ;
75-
76- for await ( const value of root . values ( ) ) {
77- if ( value . kind === ' file' ) {
78- const file = await value . getFile ( ) ;
79- content . push ( {
80- name : file . name ,
81- path : file . name ,
82- created : new Date ( file . lastModified ) . toISOString ( ) ,
83- last_modified : new Date ( file . lastModified ) . toISOString ( ) ,
84- format : ' text' ,
85- content : await file . text ( ) ,
86- writable : true ,
87- type : 'file' ,
88- mimetype : 'text/plain'
89- } ) ;
90- } else {
91- content . push ( {
92- name : value . name ,
93- path : value . name ,
94- created : new Date ( ) . toISOString ( ) ,
95- last_modified : new Date ( ) . toISOString ( ) ,
96- format : 'json' ,
97- content : null ,
98- writable : true ,
99- type : 'directory' ,
100- mimetype : 'application/json'
101- } ) ;
107+ } else {
108+ const content : Contents . IModel [ ] = [ ] ;
109+
110+ for await ( const value of localHandle . values ( ) ) {
111+ if ( value . kind === 'file' ) {
112+ const file = await value . getFile ( ) ;
113+ content . push ( {
114+ name : file . name ,
115+ path : PathExt . join ( parentPath , localPath , file . name ) ,
116+ created : new Date ( file . lastModified ) . toISOString ( ) ,
117+ last_modified : new Date ( file . lastModified ) . toISOString ( ) ,
118+ format : 'text' ,
119+ content : await file . text ( ) ,
120+ writable : true ,
121+ type : 'file' ,
122+ mimetype : 'text/plain'
123+ } ) ;
124+ } else {
125+ content . push ( {
126+ name : value . name ,
127+ path : PathExt . join ( parentPath , localPath , value . name ) ,
128+ created : '' ,
129+ last_modified : '' ,
130+ format : 'json' ,
131+ content : null ,
132+ writable : true ,
133+ type : 'directory' ,
134+ mimetype : 'application/json'
135+ } ) ;
136+ }
102137 }
103- }
104138
105- const date = new Date ( ) ;
106- return {
107- name : 'root' ,
108- path : '' ,
109- last_modified : date . toISOString ( ) ,
110- created : date . toISOString ( ) ,
111- format : 'json' ,
112- mimetype : 'application/json' ,
113- content ,
114- size : undefined ,
115- writable : true ,
116- type : 'directory'
117- } ;
139+ return {
140+ name : PathExt . basename ( parentPath ) ,
141+ path : PathExt . join ( parentPath , localPath ) ,
142+ last_modified : '' ,
143+ created : '' ,
144+ format : 'json' ,
145+ mimetype : 'application/ json' ,
146+ content ,
147+ size : undefined ,
148+ writable : true ,
149+ type : 'directory'
150+ } ;
151+ }
118152 }
119153
120- getDownloadUrl ( localPath : string ) : Promise < string > {
154+ getDownloadUrl ( path : string ) : Promise < string > {
121155 throw new Error ( 'Method not implemented.' ) ;
122156 }
123157
124158 newUntitled ( options ?: Contents . ICreateOptions ) : Promise < Contents . IModel > {
125159 throw new Error ( 'Method not implemented.' ) ;
126160 }
127161
128- delete ( localPath : string ) : Promise < void > {
162+ delete ( path : string ) : Promise < void > {
129163 throw new Error ( 'Method not implemented.' ) ;
130164 }
131165
132- rename ( oldLocalPath : string , newLocalPath : string ) : Promise < Contents . IModel > {
166+ rename ( oldPath : string , newPath : string ) : Promise < Contents . IModel > {
133167 throw new Error ( 'Method not implemented.' ) ;
134168 }
135169
136170 async save (
137- localPath : string ,
171+ path : string ,
138172 options ?: Partial < Contents . IModel >
139173 ) : Promise < Contents . IModel > {
140174 const root = this . _rootHandle ;
@@ -143,7 +177,13 @@ export class FileSystemDrive implements Contents.IDrive {
143177 throw new Error ( 'No root file handle' ) ;
144178 }
145179
146- const handle = await root . getFileHandle ( localPath ) ;
180+ let parentHandle = root ;
181+ // If saving a file that is not under root, we need the right directory handle
182+ for ( const subPath of path . split ( '/' ) . slice ( 0 , - 1 ) ) {
183+ parentHandle = await parentHandle . getDirectoryHandle ( subPath ) ;
184+ }
185+
186+ const handle = await parentHandle . getFileHandle ( PathExt . basename ( path ) ) ;
147187 const writable = await handle . createWritable ( { } ) ;
148188
149189 const format = options ?. format ;
@@ -155,25 +195,21 @@ export class FileSystemDrive implements Contents.IDrive {
155195 await writable . write ( content ) ;
156196 }
157197 await writable . close ( ) ;
158- return this . get ( localPath ) ;
198+ return this . get ( path ) ;
159199 }
160200
161- copy ( localPath : string , toLocalDir : string ) : Promise < Contents . IModel > {
201+ copy ( path : string , toLocalDir : string ) : Promise < Contents . IModel > {
162202 throw new Error ( 'Method not implemented.' ) ;
163203 }
164204
165- async createCheckpoint (
166- localPath : string
167- ) : Promise < Contents . ICheckpointModel > {
205+ async createCheckpoint ( path : string ) : Promise < Contents . ICheckpointModel > {
168206 return {
169207 id : 'test' ,
170208 last_modified : new Date ( ) . toISOString ( )
171209 } ;
172210 }
173211
174- async listCheckpoints (
175- localPath : string
176- ) : Promise < Contents . ICheckpointModel [ ] > {
212+ async listCheckpoints ( path : string ) : Promise < Contents . ICheckpointModel [ ] > {
177213 return [
178214 {
179215 id : 'test' ,
@@ -182,11 +218,11 @@ export class FileSystemDrive implements Contents.IDrive {
182218 ] ;
183219 }
184220
185- restoreCheckpoint ( localPath : string , checkpointID : string ) : Promise < void > {
221+ restoreCheckpoint ( path : string , checkpointID : string ) : Promise < void > {
186222 return Promise . resolve ( void 0 ) ;
187223 }
188224
189- deleteCheckpoint ( localPath : string , checkpointID : string ) : Promise < void > {
225+ deleteCheckpoint ( path : string , checkpointID : string ) : Promise < void > {
190226 return Promise . resolve ( void 0 ) ;
191227 }
192228
0 commit comments