73 lines
2.1 KiB
JavaScript
73 lines
2.1 KiB
JavaScript
|
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;
|