feat(archive): add rar extraction support
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Generated
+10
@@ -28,6 +28,7 @@
|
|||||||
"mammoth": "^1.12.0",
|
"mammoth": "^1.12.0",
|
||||||
"markdown-it": "^15.0.0",
|
"markdown-it": "^15.0.0",
|
||||||
"multer": "^2.2.0",
|
"multer": "^2.2.0",
|
||||||
|
"node-unrar-js": "^2.0.2",
|
||||||
"opentype.js": "^2.0.0",
|
"opentype.js": "^2.0.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"pdfjs-dist": "^6.2.108",
|
"pdfjs-dist": "^6.2.108",
|
||||||
@@ -5307,6 +5308,15 @@
|
|||||||
"devOptional": true,
|
"devOptional": true,
|
||||||
"license": "MIT"
|
"license": "MIT"
|
||||||
},
|
},
|
||||||
|
"node_modules/node-unrar-js": {
|
||||||
|
"version": "2.0.2",
|
||||||
|
"resolved": "https://registry.npmjs.org/node-unrar-js/-/node-unrar-js-2.0.2.tgz",
|
||||||
|
"integrity": "sha512-hLNmoJzqaKJnod8yiTVGe9hnlNRHotUi0CreSv/8HtfRi/3JnRC8DvsmKfeGGguRjTEulhZK6zXX5PXoVuDZ2w==",
|
||||||
|
"license": "MIT",
|
||||||
|
"engines": {
|
||||||
|
"node": ">=10.0.0"
|
||||||
|
}
|
||||||
|
},
|
||||||
"node_modules/normalize-path": {
|
"node_modules/normalize-path": {
|
||||||
"version": "3.0.0",
|
"version": "3.0.0",
|
||||||
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
"resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
|
||||||
|
|||||||
@@ -35,6 +35,7 @@
|
|||||||
"mammoth": "^1.12.0",
|
"mammoth": "^1.12.0",
|
||||||
"markdown-it": "^15.0.0",
|
"markdown-it": "^15.0.0",
|
||||||
"multer": "^2.2.0",
|
"multer": "^2.2.0",
|
||||||
|
"node-unrar-js": "^2.0.2",
|
||||||
"opentype.js": "^2.0.0",
|
"opentype.js": "^2.0.0",
|
||||||
"pdf-lib": "^1.17.1",
|
"pdf-lib": "^1.17.1",
|
||||||
"pdfjs-dist": "^6.2.108",
|
"pdfjs-dist": "^6.2.108",
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ import AdmZip from 'adm-zip';
|
|||||||
import { ZipArchive } from 'archiver';
|
import { ZipArchive } from 'archiver';
|
||||||
import * as tar from 'tar';
|
import * as tar from 'tar';
|
||||||
import _7z from '7zip-min';
|
import _7z from '7zip-min';
|
||||||
|
import { createExtractorFromFile } from 'node-unrar-js';
|
||||||
import { register } from './registry.js';
|
import { register } from './registry.js';
|
||||||
|
|
||||||
const MAX_EXTRACTED_BYTES = 2 * 1024 * 1024 * 1024;
|
const MAX_EXTRACTED_BYTES = 2 * 1024 * 1024 * 1024;
|
||||||
@@ -133,6 +134,15 @@ async function createTar7z(srcDir, outputPath, quality) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function extractRar(inputPath, destDir) {
|
||||||
|
const extractor = await createExtractorFromFile({ filepath: inputPath, targetPath: destDir });
|
||||||
|
const { files } = extractor.extract();
|
||||||
|
for (const _file of files) {
|
||||||
|
// Iterating fully is required: node-unrar-js writes each entry to disk
|
||||||
|
// lazily as this generator is advanced.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const EXTRACTORS = {
|
const EXTRACTORS = {
|
||||||
zip: extractZip,
|
zip: extractZip,
|
||||||
tar: extractTarLike,
|
tar: extractTarLike,
|
||||||
@@ -140,6 +150,7 @@ const EXTRACTORS = {
|
|||||||
'tar.bz2': extractTarBz2,
|
'tar.bz2': extractTarBz2,
|
||||||
'7z': extract7z,
|
'7z': extract7z,
|
||||||
'tar.7z': extractTar7z,
|
'tar.7z': extractTar7z,
|
||||||
|
rar: extractRar,
|
||||||
};
|
};
|
||||||
const CREATORS = {
|
const CREATORS = {
|
||||||
zip: createZip,
|
zip: createZip,
|
||||||
|
|||||||
@@ -228,3 +228,32 @@ describe('archive converters — 7z and tar.7z', () => {
|
|||||||
20000
|
20000
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
describe('archive converters — rar (extraction only)', () => {
|
||||||
|
it('lists rar as a source with the other 6 archive formats as targets, and never lists rar as a target for anything', () => {
|
||||||
|
const targets = listTargetFormats('rar');
|
||||||
|
expect(targets.sort()).toEqual(['7z', 'tar', 'tar.7z', 'tar.bz2', 'tar.gz', 'zip'].sort());
|
||||||
|
expect(listTargetFormats('zip')).not.toContain('rar');
|
||||||
|
});
|
||||||
|
|
||||||
|
it('converts the sample.rar fixture to zip', async () => {
|
||||||
|
const rarPath = path.join(import.meta.dirname, '..', 'fixtures', 'sample.rar');
|
||||||
|
const outputPath = path.join(tmpDir, 'from-rar.zip');
|
||||||
|
const entry = resolve('rar', 'zip');
|
||||||
|
|
||||||
|
await entry.convert(rarPath, outputPath);
|
||||||
|
|
||||||
|
const outZip = new AdmZip(outputPath);
|
||||||
|
expect(outZip.getEntries().length).toBeGreaterThan(0);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('archive converters — full registration matrix', () => {
|
||||||
|
it('registers all 42 source/target pairs (7 sources x 6 targets)', () => {
|
||||||
|
const sources = ['zip', 'tar', 'tar.gz', 'tar.bz2', '7z', 'tar.7z', 'rar'];
|
||||||
|
const targets = ['zip', 'tar', 'tar.gz', 'tar.bz2', '7z', 'tar.7z'];
|
||||||
|
for (const source of sources) {
|
||||||
|
expect(listTargetFormats(source).sort()).toEqual(targets.sort());
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|||||||
Vendored
+9
@@ -0,0 +1,9 @@
|
|||||||
|
# RAR fixture attribution
|
||||||
|
|
||||||
|
`sample.rar` is a byte-identical copy of `testFiles/FolderTest.rar` from the
|
||||||
|
[YuJianrong/node-unrar.js](https://github.com/YuJianrong/node-unrar.js)
|
||||||
|
repository (MIT-licensed), used there for that project's own extraction
|
||||||
|
tests. Reused here because there is no way to create a `.rar` fixture from
|
||||||
|
this codebase — the `unrar` library's license bars any tool (including this
|
||||||
|
project's own converters) from writing the RAR format, so RAR support is
|
||||||
|
extraction-only everywhere, including in test fixtures.
|
||||||
Vendored
BIN
Binary file not shown.
Reference in New Issue
Block a user