@guoyunhe/downloader
Version:
Download large files with minimum RAM usage. Support tar.gz and zip extraction.
141 lines (137 loc) • 5.49 kB
JavaScript
;
var __create = Object.create;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __getProtoOf = Object.getPrototypeOf;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
// If the importer is in node compatibility mode or this is not an ESM
// file that has been converted to a CommonJS file using a Babel-
// compatible transform (i.e. "__esModule" has not been set), then set
// "default" to the CommonJS "module.exports" for node compatibility.
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
mod
));
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var src_exports = {};
__export(src_exports, {
download: () => download
});
module.exports = __toCommonJS(src_exports);
var import_fs = require("fs");
var import_promises2 = require("fs/promises");
var import_http = __toESM(require("http"));
var import_https = __toESM(require("https"));
var import_node_stream_zip = __toESM(require("node-stream-zip"));
var import_os = require("os");
var import_path2 = require("path");
var import_tar = __toESM(require("tar"));
// src/stripDirectory.ts
var import_promises = require("fs/promises");
var import_path = require("path");
async function stripDirectory(dir, strip = 1) {
if (strip < 1)
return;
const items = await scanDirectory(dir, strip + 1);
const tmpDir = dir + "__old__";
await (0, import_promises.rename)(dir, tmpDir);
await (0, import_promises.mkdir)(dir);
await Promise.all(
Object.entries(items).map(async ([from, to]) => {
(0, import_promises.rename)((0, import_path.join)(tmpDir, from), (0, import_path.join)(dir, to));
})
);
await (0, import_promises.rm)(tmpDir, { recursive: true });
}
async function scanDirectory(dir, depth = 1, prefix = "") {
const result = {};
if (depth < 1)
return result;
const items = await (0, import_promises.readdir)(dir, { withFileTypes: true });
for (let i = 0; i < items.length; i++) {
const item = items[i];
if (item.isDirectory() && depth > 1) {
Object.assign(
result,
await scanDirectory((0, import_path.join)(dir, item.name), depth - 1, (0, import_path.join)(prefix, item.name))
);
} else {
result[(0, import_path.join)(prefix, item.name)] = item.name;
}
}
return result;
}
// src/index.ts
function download(url, dist, options = {}) {
const { maxRedirects = 5, extract = false, strip = 0 } = options;
return new Promise((resolve, reject) => {
const get = url.startsWith("https://") ? import_https.default.get : import_http.default.get;
get(url, async (res) => {
if (res.statusCode && res.statusCode >= 200 && res.statusCode < 300) {
let output;
let zipFile;
if (extract && [
"application/tar",
"application/tar+gzip",
"application/x-tar",
"application/x-gzip"
].includes(res.headers["content-type"] || "")) {
await (0, import_promises2.mkdir)(dist, { recursive: true });
output = import_tar.default.x({
strip,
cwd: dist
});
} else if (extract && res.headers["content-type"] === "application/zip") {
const tmpPrefix = (0, import_path2.join)((0, import_os.tmpdir)(), "guoyunhe-downloader");
const temp = await (0, import_promises2.mkdtemp)(tmpPrefix);
zipFile = (0, import_path2.join)(temp, "archive.zip");
output = (0, import_fs.createWriteStream)(zipFile);
} else {
await (0, import_promises2.mkdir)((0, import_path2.dirname)(dist), { recursive: true });
output = (0, import_fs.createWriteStream)(dist);
}
res.pipe(output, { end: false });
res.on("end", async function() {
output.end();
if (zipFile) {
const zip = new import_node_stream_zip.default.async({ file: zipFile });
await zip.extract(null, dist);
await stripDirectory(dist, strip);
}
resolve();
});
} else if (res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
if (maxRedirects > 0) {
const newUrl = res.headers.location;
download(newUrl, dist, { ...options, maxRedirects: maxRedirects - 1 }).then(resolve).catch(reject);
} else {
reject(
new Error(
`Too many redirects when downloading ${url}. Use maxRedirects option to increase the limit.`
)
);
}
} else {
reject(new Error("Failed to download " + url));
}
});
});
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
download
});