unpkg/server/utils/fetchPackage.js

73 lines
2.1 KiB
JavaScript
Raw Normal View History

2018-05-21 22:44:00 +00:00
require("isomorphic-fetch");
const fs = require("fs");
const mkdirp = require("mkdirp");
const gunzip = require("gunzip-maybe");
const tar = require("tar-fs");
const createTempPath = require("./createTempPath");
function stripNamePrefix(headers) {
// 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.
headers.name = headers.name.replace(/^[^/]+\//, "");
return headers;
}
function ignoreLinks(file, headers) {
return headers.type === "link" || headers.type === "symlink";
}
function extractResponse(response, outputDir) {
return new Promise((resolve, reject) => {
const extract = tar.extract(outputDir, {
readable: true, // All dirs/files should be readable.
map: stripNamePrefix,
ignore: ignoreLinks
});
response.body
.pipe(gunzip())
.pipe(extract)
.on("finish", resolve)
.on("error", reject);
});
}
function fetchPackage(packageConfig) {
return new Promise((resolve, reject) => {
const tarballURL = packageConfig.dist.tarball;
const outputDir = createTempPath(packageConfig.name, packageConfig.version);
console.log(`info: Fetching ${tarballURL} and extracting to ${outputDir}`);
fs.access(outputDir, error => {
if (error) {
if (error.code === "ENOENT" || error.code === "ENOTDIR") {
// ENOENT or ENOTDIR are to be expected when we haven't yet
// fetched a package for the first time. Carry on!
mkdirp(outputDir, error => {
if (error) {
reject(error);
} else {
resolve(
fetch(tarballURL)
.then(res => extractResponse(res, outputDir))
.then(() => outputDir)
);
}
});
} else {
reject(error);
}
} else {
// Best case: we already have this package cached on disk!
resolve(outputDir);
}
});
});
}
module.exports = fetchPackage;