62 lines
1.8 KiB
JavaScript
62 lines
1.8 KiB
JavaScript
|
import gunzip from 'gunzip-maybe';
|
||
|
import tar from 'tar-stream';
|
||
|
|
||
|
import bufferStream from '../utils/bufferStream.js';
|
||
|
import getContentType from '../utils/getContentType.js';
|
||
|
import getIntegrity from '../utils/getIntegrity.js';
|
||
|
import { getPackage } from '../utils/npm.js';
|
||
|
|
||
|
async function findEntry(stream, filename) {
|
||
|
// filename = /some/file/name.js
|
||
|
return new Promise((accept, reject) => {
|
||
|
let foundEntry = null;
|
||
|
|
||
|
stream
|
||
|
.pipe(gunzip())
|
||
|
.pipe(tar.extract())
|
||
|
.on('error', reject)
|
||
|
.on('entry', async (header, stream, next) => {
|
||
|
const entry = {
|
||
|
// Most packages have header names that look like `package/index.js`
|
||
|
// so we shorten that to just `/index.js` here. A few packages use a
|
||
|
// prefix other than `package/`. e.g. the firebase package uses the
|
||
|
// `firebase_npm/` prefix. So we just strip the first dir name.
|
||
|
path: header.name.replace(/^[^/]+/, ''),
|
||
|
type: header.type
|
||
|
};
|
||
|
|
||
|
// Ignore non-files and files that don't match the name.
|
||
|
if (entry.type !== 'file' || entry.path !== filename) {
|
||
|
stream.resume();
|
||
|
stream.on('end', next);
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
const content = await bufferStream(stream);
|
||
|
|
||
|
entry.contentType = getContentType(entry.path);
|
||
|
entry.integrity = getIntegrity(content);
|
||
|
entry.lastModified = header.mtime.toUTCString();
|
||
|
entry.size = content.length;
|
||
|
|
||
|
foundEntry = entry;
|
||
|
|
||
|
next();
|
||
|
})
|
||
|
.on('finish', () => {
|
||
|
accept(foundEntry);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
export default async function serveFileMetadata(req, res) {
|
||
|
const stream = await getPackage(req.packageName, req.packageVersion);
|
||
|
const entry = await findEntry(stream, req.filename);
|
||
|
|
||
|
if (!entry) {
|
||
|
// TODO: 404
|
||
|
}
|
||
|
|
||
|
res.send(entry);
|
||
|
}
|