Skip to content
This repository has been archived by the owner on Dec 15, 2022. It is now read-only.

Support for .zip archives with unknown extensions at runtime #5

Open
wants to merge 7 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@ lib/
npm-debug.log
.node-version
.DS_Store
.idea
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,14 @@ Buffer contents of the uncompressed paths. The `callback` gets two arguments
`callback` - The function to call after reading completes with an error or
the Buffer contents.

### archive.configureExtensions(additionalExtensions)

Adds custom file extensions to zip, tar, and/or gzip handling to support archives with atypical extensions.

`additionalExtensions` - Object literal with string arrays for zip, tar, and gzip

Example: `archive.configureExtensions(zip: ['.docx'])`

### ArchiveEntry

Class representing a path entry inside an archive file.
Expand Down
Binary file added spec/fixtures/one-file.customZip
Binary file not shown.
Binary file added spec/fixtures/word.docx
Binary file not shown.
25 changes: 25 additions & 0 deletions spec/zip-spec.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,20 @@ describe "zip files", ->
waitsFor -> pathError?
runs -> expect(pathError.message.length).toBeGreaterThan 0

describe "listing when custom extensions are added", ->
it "lists files and folders in the docx archive", ->
zipPaths = null
callback = (error, paths) -> zipPaths = paths
archive.configureExtensions(zip: ['.docx'])
archive.list(path.join(fixturesRoot, 'word.docx'), forceZip: true, callback)
waitsFor -> zipPaths?
runs ->
expect(zipPaths.length).toBe 13
expect(zipPaths[0].path).toBe '[Content_Types].xml'
expect(zipPaths[0].isDirectory()).toBe false
expect(zipPaths[0].isFile()).toBe true
expect(zipPaths[0].isSymbolicLink()).toBe false

describe ".readFile()", ->
describe "when the path exists in the archive", ->
it "calls back with the contents of the given path", ->
Expand Down Expand Up @@ -116,3 +130,14 @@ describe "zip files", ->
archive.readFile(archivePath, "folder#{path.sep}", callback)
waitsFor -> pathError?
runs -> expect(pathError.message.length).toBeGreaterThan 0

describe "reading when custom extensions are added", ->
it "calls back with the contents of the given path", ->
archivePath = path.join(fixturesRoot, 'one-file.customZip')
pathContents = null
callback = (error, contents) -> pathContents = contents
archive.configureExtensions(zip: ['.customZip'])
archive.readFile(archivePath, 'file.txt', callback)
waitsFor -> pathContents?
runs -> expect(pathContents.toString()).toBe 'hello\n'

39 changes: 25 additions & 14 deletions src/ls-archive.coffee
Original file line number Diff line number Diff line change
Expand Up @@ -172,43 +172,54 @@ readEntry = (entry, callback) ->
entry.on 'data', (data) -> contents.push(data)
entry.on 'end', -> callback(null, Buffer.concat(contents))

isTarPath = (archivePath) ->
path.extname(archivePath) is '.tar'
isTarPath = (archivePath, extensions) ->
path.extname(archivePath) in extensions

isZipPath = (archivePath) ->
isZipPath = (archivePath, extensions) ->
extension = path.extname(archivePath)
extension in ['.epub', '.jar', '.love', '.war', '.zip', '.egg', '.whl', '.xpi', '.nupkg']
extension in extensions

isGzipPath = (archivePath) ->
path.extname(archivePath) is '.tgz' or
path.extname(path.basename(archivePath, '.gz')) is '.tar'
isGzipPath = (archivePath, extensions) ->
path.extname(archivePath) in extensions.gzip or
path.extname(path.basename(archivePath, '.gz')) in extensions.tar

module.exports =

extensions:
zip: ['.epub', '.jar', '.love', '.war', '.zip', '.egg', '.whl', '.xpi', '.nupkg']
tar: ['.tar']
gzip: ['.tgz']

configureExtensions: (addlExtensions) ->
@extensions.zip.push(addlExtensions.zip...)
@extensions.tar.push(addlExtensions.tar...)
@extensions.gzip.push(addlExtensions.gzip...)

isPathSupported: (archivePath) ->
return false unless archivePath
isTarPath(archivePath) or isZipPath(archivePath) or isGzipPath(archivePath)
isTarPath(archivePath, @extensions.tar) or isZipPath(archivePath, @extensions.zip) or isGzipPath(archivePath, @extensions)

list: (archivePath, options={}, callback) ->
if typeof options is 'function'
callback = options
options = {}

if isTarPath(archivePath)
if isTarPath(archivePath, @extensions.tar)
listTar(archivePath, options, wrapCallback(callback))
else if isGzipPath(archivePath)
else if isGzipPath(archivePath, @extensions)
listGzip(archivePath, options, wrapCallback(callback))
else if isZipPath(archivePath)
else if isZipPath(archivePath, @extensions.zip)
listZip(archivePath, options, wrapCallback(callback))
else
callback(new Error("'#{path.extname(archivePath)}' files are not supported"))
undefined

readFile: (archivePath, filePath, callback) ->
if isTarPath(archivePath)
if isTarPath(archivePath, @extensions.tar)
readFileFromTar(archivePath, filePath, wrapCallback(callback))
else if isGzipPath(archivePath)
else if isGzipPath(archivePath, @extensions)
readFileFromGzip(archivePath, filePath, wrapCallback(callback))
else if isZipPath(archivePath)
else if isZipPath(archivePath, @extensions.zip)
readFileFromZip(archivePath, filePath, wrapCallback(callback))
else
callback(new Error("'#{path.extname(archivePath)}' files are not supported"))
Expand Down