@@ -24,45 +24,64 @@ export const checkDirectoryName = (dirName: string) => !!!dirName.match(invalidD
2424
2525export interface ZipMethods {
2626 isZipRoot : ( path : string ) => boolean
27- getEntries : ( ) => Promise < ZipEntry [ ] >
27+ getEntries : ( path : string ) => Promise < ZipEntry [ ] >
28+ getRelativePath : ( path : string ) => string
29+ prepareEntries : ( ) => Promise < void >
30+ getFileDescriptor : ( entry : ZipEntry ) => FileDescriptor
31+ isDir : ( path : string ) => boolean
2832}
2933
30- export class Zip {
34+ export class Zip implements ZipMethods {
3135 ready = false
3236 zip : StreamZipAsync
3337 zipEntries : ZipEntry [ ]
3438 zipPath : string
3539 zipFilename : string
3640
3741 constructor ( path : string ) {
38- this . zip = new StreamZip . async ( { file : path } )
42+ this . zipPath = path . replace ( / (?< = \. z i p ) .* / i, '' )
43+ this . zip = new StreamZip . async ( { file : this . zipPath } )
3944 this . zipEntries = [ ]
40- this . zipPath = path . replace ( / ( \/ $ ) * / , '' )
4145 this . zipFilename = ''
4246 }
4347
4448 isZipRoot ( path : string ) {
4549 return true
4650 }
4751
48- async getEntries ( path : string ) {
52+ /**
53+ * Get path relative to zip path
54+ */
55+ getRelativePath ( path : string ) {
56+ return path . replace ( this . zipPath , '' ) . replace ( / ^ \/ / , '' )
57+ }
58+
59+ async prepareEntries ( ) {
4960 if ( ! this . ready ) {
5061 const entries = await this . zip . entries ( )
5162 this . zipEntries = Object . values ( entries )
5263 this . ready = true
5364 }
65+ }
66+
67+ async getEntries ( path : string ) {
68+ const pathInZip = this . getRelativePath ( path )
69+ // const isZipRoot = !pathInZip.length
5470
55- const pathInZip = path . replace ( this . zipPath , '' )
56- const isZipRoot = ! ! ! pathInZip
71+ // const longestPath = pathInZip.replace(/([^\/]*)$/, '')
72+ const regExp = pathInZip . length ? new RegExp ( `^${ pathInZip } \/([^\/]+)[\/]?$` , 'g' ) : / ^ ( [ ^ \/ ] ) * [ \/ ] ? $ / g
73+ return this . zipEntries . filter ( ( entry ) => ! ! entry . name . match ( regExp ) )
74+ }
5775
58- // TODO: get path inside zip
59- return this . zipEntries . filter ( ( entry ) => {
60- if ( isZipRoot ) {
61- const name = entry . name . replace ( / ( \/ $ ) * / g, '' )
62- // root: include files in root zip
63- return ! name . match ( / \/ / )
64- }
65- } )
76+ isDir ( path : string ) {
77+ const pathInZip = this . getRelativePath ( path )
78+ const longestPath = pathInZip . replace ( / ( [ ^ \/ ] * ) $ / , '' )
79+
80+ if ( ! longestPath . length ) {
81+ return true
82+ } else {
83+ return this . zipEntries . some ( ( entry ) => entry . isDirectory && ! ! entry . name . match ( pathInZip ) )
84+ }
6685 }
6786
6887 getFileDescriptor ( entry : ZipEntry ) {
@@ -72,10 +91,9 @@ export class Zip {
7291 const mDate = new Date ( entry . time )
7392 const mode = entry . attr ? ( ( entry . attr >>> 0 ) | 0 ) >> 16 : 0
7493
75- // TODO: build file
7694 const file = {
7795 dir : path . parse ( `${ this . zipPath } /${ name } ` ) . dir ,
78- fullname : name ,
96+ fullname : parsed . base ,
7997 name : parsed . name ,
8098 extension,
8199 cDate : mDate ,
@@ -145,20 +163,27 @@ export class ZipApi implements FsApi {
145163 }
146164
147165 resolve ( newPath : string ) : string {
148- // TODO:
149166 // gh#44: replace ~ with userpath
150- debugger
151167 const dir = newPath . replace ( / ^ ~ / , HOME_DIR )
152168 return path . resolve ( dir )
153169 }
154170
155171 async cd ( path : string , transferId = - 1 ) : Promise < string > {
156172 const resolvedPath = this . resolve ( path )
173+
174+ try {
175+ await this . zip . prepareEntries ( )
176+ } catch ( e ) {
177+ console . error ( 'error getting zip file entries' , e )
178+ throw { code : 'ENOTDIR' }
179+ }
180+
157181 try {
158182 const isDir = await this . isDir ( resolvedPath )
159183 if ( isDir ) {
160184 return resolvedPath
161185 } else {
186+ debugger
162187 throw { code : 'ENOTDIR' }
163188 }
164189 } catch { }
@@ -286,7 +311,7 @@ export class ZipApi implements FsApi {
286311
287312 async isDir ( path : string , transferId = - 1 ) : Promise < boolean > {
288313 console . warn ( 'TODO: FsZip.isDir => check that file inside dir is a directory' )
289- return true
314+ return this . zip . isDir ( path )
290315 }
291316
292317 async exists ( path : string , transferId = - 1 ) : Promise < boolean > {
@@ -356,102 +381,9 @@ export class ZipApi implements FsApi {
356381
357382 async list ( dir : string , watchDir = false , transferId = - 1 ) : Promise < FileDescriptor [ ] > {
358383 const entries = await this . zip . getEntries ( dir )
359- // return []
360- return entries . map ( ( entry ) => this . zip . getFileDescriptor ( entry ) )
361- debugger
362- throw 'TODO: FsZip.list not implemented'
363- // try {
364- // await this.isDir(dir)
365- // return new Promise<FileDescriptor[]>((resolve, reject) => {
366- // vol.readdir(dir, (err, items) => {
367- // if (err) {
368- // reject(err)
369- // } else {
370- // const dirPath = path.resolve(dir)
371-
372- // const files: FileDescriptor[] = []
373-
374- // for (let i = 0; i < items.length; i++) {
375- // const file = ZipApi.fileFromPath(path.join(dirPath, items[i] as string))
376- // files.push(file)
377- // }
378-
379- // watchDir && this.onList(dirPath)
380384
381- // resolve(files)
382- // }
383- // })
384- // })
385- // } catch (err) {
386- // throw {
387- // code: err.code,
388- // message: `Could not access path: ${dir}`,
389- // }
390- // }
391- }
392-
393- static fileFromPath ( fullPath : string ) : FileDescriptor {
394- const format = path . parse ( fullPath )
395- const name = fullPath
396- const stats : Partial < BigIntStats > = null
397-
398- debugger
399-
400- return {
401- name : `${ fullPath } ` ,
402- } as FileDescriptor
403- // try {
404- // // do not follow symlinks first
405- // stats = vol.lstatSync(fullPath, { bigint: true })
406- // if (stats.isSymbolicLink()) {
407- // // get link target path first
408- // name = vol.readlinkSync(fullPath) as string
409- // targetStats = vol.statSync(fullPath, { bigint: true })
410- // }
411- // } catch (err) {
412- // console.warn('error getting stats for', fullPath, err)
413-
414- // const isDir = stats ? stats.isDirectory() : false
415- // const isSymLink = stats ? stats.isSymbolicLink() : false
416-
417- // stats = {
418- // ctime: new Date(),
419- // mtime: new Date(),
420- // birthtime: new Date(),
421- // size: stats ? stats.size : 0n,
422- // isDirectory: (): boolean => isDir,
423- // mode: -1n,
424- // isSymbolicLink: (): boolean => isSymLink,
425- // ino: 0n,
426- // dev: 0n,
427- // }
428- // }
429-
430- // const extension = path.parse(name).ext.toLowerCase()
431- // const mode = targetStats ? targetStats.mode : stats.mode
432-
433- // const file: FileDescriptor = {
434- // dir: format.dir,
435- // fullname: format.base,
436- // name: format.name,
437- // extension: extension,
438- // cDate: stats.ctime,
439- // mDate: stats.mtime,
440- // bDate: stats.birthtime,
441- // length: Number(stats.size),
442- // mode: Number(mode),
443- // isDir: targetStats ? targetStats.isDirectory() : stats.isDirectory(),
444- // readonly: false,
445- // type:
446- // (!(targetStats ? targetStats.isDirectory() : stats.isDirectory()) &&
447- // filetype(Number(mode), 0, 0, extension)) ||
448- // '',
449- // isSym: stats.isSymbolicLink(),
450- // target: (stats.isSymbolicLink() && name) || null,
451- // id: MakeId({ ino: stats.ino, dev: stats.dev }),
452- // }
453-
454- // return file
385+ return entries . map ( ( entry ) => this . zip . getFileDescriptor ( entry ) )
386+ // FIXME: what should we do about watch dir?
455387 }
456388
457389 isRoot ( path : string ) : boolean {
0 commit comments