85 lines
2.4 KiB
JavaScript
85 lines
2.4 KiB
JavaScript
|
import gunzip from 'gunzip-maybe';
|
||
|
import tar from 'tar-stream';
|
||
|
|
||
|
import bufferStream from '../utils/bufferStream.js';
|
||
|
import createDataURI from '../utils/createDataURI.js';
|
||
|
import getContentType from '../utils/getContentType.js';
|
||
|
import getIntegrity from '../utils/getIntegrity.js';
|
||
|
import { getPackage } from '../utils/npm.js';
|
||
|
import getHighlights from '../utils/getHighlights.js';
|
||
|
import getLanguageName from '../utils/getLanguageName.js';
|
||
|
|
||
|
import serveBrowsePage from './serveBrowsePage.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;
|
||
|
}
|
||
|
|
||
|
entry.content = await bufferStream(stream);
|
||
|
foundEntry = entry;
|
||
|
|
||
|
next();
|
||
|
})
|
||
|
.on('finish', () => {
|
||
|
accept(foundEntry);
|
||
|
});
|
||
|
});
|
||
|
}
|
||
|
|
||
|
export default async function serveFileBrowser(req, res) {
|
||
|
const stream = await getPackage(req.packageName, req.packageVersion);
|
||
|
const entry = await findEntry(stream, req.filename);
|
||
|
|
||
|
if (!entry) {
|
||
|
return res.status(404).send(`Not found: ${req.packageSpec}${req.filename}`);
|
||
|
}
|
||
|
|
||
|
const details = {
|
||
|
contentType: getContentType(entry.path),
|
||
|
integrity: getIntegrity(entry.content),
|
||
|
language: getLanguageName(entry.path),
|
||
|
size: entry.content.length
|
||
|
};
|
||
|
|
||
|
if (/^image\//.test(details.contentType)) {
|
||
|
details.uri = createDataURI(details.contentType, entry.content);
|
||
|
details.highlights = null;
|
||
|
} else {
|
||
|
details.uri = null;
|
||
|
details.highlights = getHighlights(
|
||
|
entry.content.toString('utf8'),
|
||
|
entry.path
|
||
|
);
|
||
|
}
|
||
|
|
||
|
req.browseTarget = {
|
||
|
path: req.filename,
|
||
|
type: 'file',
|
||
|
details
|
||
|
};
|
||
|
|
||
|
serveBrowsePage(req, res);
|
||
|
}
|