diff --git a/bin/asar.js b/bin/asar.js index c3d094e1..f0f0a281 100755 --- a/bin/asar.js +++ b/bin/asar.js @@ -55,13 +55,32 @@ program.command('list ') } }) -program.command('extract-file ') - .alias('ef') - .description('extract one file from archive') - .action(function (archive, filename) { - require('fs').writeFileSync(require('path').basename(filename), - asar.extractFile(archive, filename)) - }) + program + .command('extract-file [destination]') + .alias('ef') + .description( + 'extract one file from archive. Optionally output to (already-existing) destination folder, otherwise cwd will be used', + ) + .action(function (archive, filename, destination) { + const path = require('path'); + const fs = require('fs'); + + // extract first to memory so that error is thrown if file does not exist + const fileData = asar.extractFile(archive, filename) + const file = path.basename(filename); + destination = destination?.trim() + const out = destination ? path.join(destination, file) : file; + + // check if destination exists. when destination is not provided, we will write to cwd + if (destination && !fs.existsSync(destination)) { + throw new Error("destination directory does not exist, please create before attempting extraction") + } + if (fs.existsSync(out)) { + throw new Error("destination file already exists") + } + fs.writeFileSync(out, fileData); + }); + program.command('extract ') .alias('e') diff --git a/test/cli-spec.js b/test/cli-spec.js index 1c0f20fd..75c19483 100644 --- a/test/cli-spec.js +++ b/test/cli-spec.js @@ -76,27 +76,32 @@ describe('command line interface', function () { 'test/expected/packthis-unicode-path-filelist.txt', ); }); - // we need a way to set a path to extract to first, otherwise we pollute our project dir - // or we fake it by setting our cwd, but I don't like that - /* it('should extract a text file from archive', async () => { - await execAsar('ef test/input/extractthis.asar dir1/file1.txt') - const actual = await fs.readFile('tmp/file1.txt', 'utf8') - let expected = await fs.readFile('test/expected/extractthis/dir1/file1.txt', 'utf8') + fs.mkdirSync(TEST_APPS_DIR); + await execAsar(`ef test/input/extractthis.asar dir1/file1.txt ${TEST_APPS_DIR}`); + const actual = await fs.readFile(path.join(TEST_APPS_DIR, 'file1.txt'), 'utf8'); + let expected = await fs.readFile('test/expected/extractthis/dir1/file1.txt', 'utf8'); // on windows replace crlf with lf if (os.platform() === 'win32') { - expected = expected.replace(/\r\n/g, '\n') + expected = expected.replace(/\r\n/g, '\n'); } - assert.strictEqual(actual, expected) - }) + assert.strictEqual(actual, expected); + }); + it('should extract a binary file from archive', async () => { + fs.mkdirSync(TEST_APPS_DIR); + await execAsar(`ef test/input/extractthis.asar dir2/file2.png ${TEST_APPS_DIR}`); + await compFiles( + path.join(TEST_APPS_DIR, 'file2.png'), + 'test/expected/extractthis/dir2/file2.png', + ); + }); + it('should throw error when extracting file to non-existant destination directory', async () => { + await assert.rejects( + execAsar(`ef test/input/extractthis.asar dir2/file2.png ${TEST_APPS_DIR}`), + /destination directory does not exist, please create before attempting extraction/, + ); + }); - it('should extract a binary file from archive', async () => { - await execAsar('ef test/input/extractthis.asar dir2/file2.png') - const actual = await fs.readFile('tmp/file2.png', 'utf8') - const expected = await fs.readFile('test/expected/extractthis/dir2/file2.png', 'utf8') - assert.strictEqual(actual, expected) - }) - */ it('should extract an archive', async () => { await execAsar('e test/input/extractthis.asar tmp/extractthis-cli/'); return compDirs('tmp/extractthis-cli/', 'test/expected/extractthis');