@zag-js/file-utils
Version:
JS File API utilities
230 lines (218 loc) • 12.6 kB
JavaScript
// src/data-transfer.ts
var getItemEntry = (item) => typeof item.getAsEntry === "function" ? item.getAsEntry() : typeof item.webkitGetAsEntry === "function" ? item.webkitGetAsEntry() : null;
var isDirectoryEntry = (entry) => entry.isDirectory;
var isFileEntry = (entry) => entry.isFile;
var addRelativePath = (file, path) => {
Object.defineProperty(file, "relativePath", { value: path ? `${path}/${file.name}` : file.name });
return file;
};
var getFileEntries = (items, traverseDirectories) => Promise.all(
Array.from(items).filter((item) => item.kind === "file").map((item) => {
const entry = getItemEntry(item);
if (!entry) return null;
if (isDirectoryEntry(entry) && traverseDirectories) {
return getDirectoryFiles(entry.createReader(), `${entry.name}`);
}
if (isFileEntry(entry) && typeof item.getAsFile === "function") {
const file = item.getAsFile();
return Promise.resolve(file ? addRelativePath(file, "") : null);
}
if (isFileEntry(entry)) {
return new Promise((resolve) => {
entry.file((file) => {
resolve(addRelativePath(file, ""));
});
});
}
}).filter((b) => b)
);
var getDirectoryFiles = (reader, path = "") => new Promise((resolve) => {
const entryPromises = [];
const readDirectoryEntries = () => {
reader.readEntries((entries) => {
if (entries.length === 0) {
resolve(Promise.all(entryPromises).then((entries2) => entries2.flat()));
return;
}
const promises = entries.map((entry) => {
if (!entry) return null;
if (isDirectoryEntry(entry)) {
return getDirectoryFiles(entry.createReader(), `${path}${entry.name}`);
}
if (isFileEntry(entry)) {
return new Promise((resolve2) => {
entry.file((file) => {
resolve2(addRelativePath(file, path));
});
});
}
}).filter((b) => b);
entryPromises.push(Promise.all(promises));
readDirectoryEntries();
});
};
readDirectoryEntries();
});
// src/data-url-to-blob.ts
function dataURItoBlob(uri) {
const binary = atob(uri.split(",")[1]);
const mimeString = uri.split(",")[0].split(":")[1].split(";")[0];
const buffer = new ArrayBuffer(binary.length);
const intArray = new Uint8Array(buffer);
for (let i = 0; i < binary.length; i++) {
intArray[i] = binary.charCodeAt(i);
}
return new Blob([buffer], { type: mimeString });
}
// src/download-file.ts
var BOM_REGEX = /^\s*(?:text\/\S*|application\/xml|\S*\/\S*\+xml)\s*;.*charset\s*=\s*utf-8/i;
var MAC_REGEX = /Macintosh/;
var APPLE_WEBKIT_REGEX = /AppleWebKit/;
var SAFARI_REGEX = /Safari/;
function getBlob(blobOrString, type, appendBOM) {
let blob = typeof blobOrString === "string" ? new Blob([blobOrString], { type }) : blobOrString;
if (appendBOM && BOM_REGEX.test(blob.type)) {
return new Blob([String.fromCharCode(65279), blob], { type: blob.type });
}
return blob;
}
function isMSEdge(win) {
return Boolean(win.navigator && win.navigator.msSaveOrOpenBlob);
}
function isWebView(win) {
return win.navigator && MAC_REGEX.test(win.navigator.userAgent) && APPLE_WEBKIT_REGEX.test(win.navigator.userAgent) && !SAFARI_REGEX.test(win.navigator.userAgent);
}
function downloadFile(options) {
const { file, win = window, type, name, appendBOM, revokeTimeout = 0 } = options;
const doc = win.document;
const blob = getBlob(file, type, appendBOM);
const fileName = (file instanceof File ? name || file.name : name) || "file-download";
if (isMSEdge(win)) {
win.navigator.msSaveOrOpenBlob(blob, fileName);
return;
}
const isMacOSWebView = isWebView(win);
const anchor = doc.createElement("a");
const canUseDownload = "download" in anchor && !isMacOSWebView;
if (canUseDownload) {
const url2 = win.URL.createObjectURL(blob);
anchor.href = url2;
anchor.rel = "noopener";
anchor.download = fileName;
anchor.style.display = "none";
doc.body.appendChild(anchor);
anchor.dispatchEvent(new win.MouseEvent("click"));
setTimeout(() => {
win.URL.revokeObjectURL(url2);
anchor.remove();
}, revokeTimeout);
return;
}
const url = win.URL.createObjectURL(blob);
const popup = win.open(url, "_blank");
if (!popup) {
win.location.href = url;
}
setTimeout(() => {
win.URL.revokeObjectURL(url);
}, revokeTimeout);
}
// src/get-accept-attr.ts
function isMIMEType(v) {
return v === "audio/*" || v === "video/*" || v === "image/*" || v === "text/*" || /\w+\/[-+.\w]+/g.test(v);
}
function isExt(v) {
return /^.*\.[\w]+$/.test(v);
}
var isValidMIME = (v) => isMIMEType(v) || isExt(v);
function getAcceptAttrString(accept) {
if (accept == null) return;
if (typeof accept === "string") {
return accept;
}
if (Array.isArray(accept)) {
return accept.filter(isValidMIME).join(",");
}
return Object.entries(accept).reduce((a, [mimeType, ext]) => [...a, mimeType, ...ext], []).filter(isValidMIME).join(",");
}
// src/get-file-data-url.ts
var getFileDataUrl = async (file) => {
const reader = new FileReader();
return new Promise((resolve, reject) => {
reader.onerror = () => {
reader.abort();
reject(new Error("There was an error reading a file"));
};
reader.onloadend = () => {
const { result } = reader;
if (result instanceof ArrayBuffer) {
reject(new Error("Expected DataURL as string from Blob/File, got ArrayBuffer"));
} else {
resolve(result || void 0);
}
};
reader.readAsDataURL(file);
});
};
// src/get-total-file-size.ts
var getTotalFileSize = (files) => {
return files.reduce((acc, file) => acc + file.size, 0);
};
// src/is-file-equal.ts
var isFileEqual = (file1, file2) => {
return file1.name === file2.name && file1.size === file2.size && file1.type === file2.type;
};
// src/is-valid-file-size.ts
var isDefined = (v) => v !== void 0 && v !== null;
function isValidFileSize(file, minSize, maxSize) {
if (isDefined(file.size)) {
if (isDefined(minSize) && isDefined(maxSize)) {
if (file.size > maxSize) return [false, "FILE_TOO_LARGE"];
if (file.size < minSize) return [false, "FILE_TOO_SMALL"];
} else if (isDefined(minSize) && file.size < minSize) {
return [false, "FILE_TOO_SMALL"];
} else if (isDefined(maxSize) && file.size > maxSize) {
return [false, "FILE_TOO_LARGE"];
}
}
return [true, null];
}
// src/mime-types.ts
var mimeTypes = "3g2_video/3gpp2[3gp,3gpp_video/3gpp[3mf_model/3mf[7z_application/x-7z-compressed[aac_audio/aac[ac_application/pkix-attr-cert[adp_audio/adpcm[adts_audio/aac[ai_application/postscript[aml_application/automationml-aml+xml[amlx_application/automationml-amlx+zip[amr_audio/amr[apk_application/vnd.android.package-archive[apng_image/apng[appcache,manifest_text/cache-manifest[appinstaller_application/appinstaller[appx_application/appx[appxbundle_application/appxbundle[asc_application/pgp-keys[atom_application/atom+xml[atomcat_application/atomcat+xml[atomdeleted_application/atomdeleted+xml[atomsvc_application/atomsvc+xml[au,snd_audio/basic[avi_video/x-msvideo[avci_image/avci[avcs_image/avcs[avif_image/avif[aw_application/applixware[bdoc_application/bdoc[bin,bpk,buffer,deb,deploy,dist,distz,dll,dmg,dms,dump,elc,exe,img,iso,lrf,mar,msi,msm,msp,pkg,so_application/octet-stream[bmp,dib_image/bmp[btf,btif_image/prs.btif[bz2_application/x-bzip2[c_text/x-c[ccxml_application/ccxml+xml[cdfx_application/cdfx+xml[cdmia_application/cdmi-capability[cdmic_application/cdmi-container[cdmid_application/cdmi-domain[cdmio_application/cdmi-object[cdmiq_application/cdmi-queue[cer_application/pkix-cert[cgm_image/cgm[cjs_application/node[class_application/java-vm[coffee,litcoffee_text/coffeescript[conf,def,in,ini,list,log,text,txt_text/plain[cpp,cxx,cc_text/x-c++src[cpl_application/cpl+xml[cpt_application/mac-compactpro[crl_application/pkix-crl[css_text/css[csv_text/csv[cu_application/cu-seeme[cwl_application/cwl[cww_application/prs.cww[davmount_application/davmount+xml[dbk_application/docbook+xml[doc_application/msword[docx_application/vnd.openxmlformats-officedocument.wordprocessingml.document[dsc_text/prs.lines.tag[dssc_application/dssc+der[dtd_application/xml-dtd[dwd_application/atsc-dwd+xml[ear,jar,war_application/java-archive[ecma_application/ecmascript[emf_image/emf[eml,mime_message/rfc822[emma_application/emma+xml[emotionml_application/emotionml+xml[eot_application/vnd.ms-fontobject[eps,ps_application/postscript[epub_application/epub+zip[exi_application/exi[exp_application/express[exr_image/aces[ez_application/andrew-inset[fdf_application/fdf[fdt_application/fdt+xml[fits_image/fits[flac_audio/flac[flv_video/x-flv[g3_image/g3fax[geojson_application/geo+json[gif_image/gif[glb_model/gltf-binary[gltf_model/gltf+json[gml_application/gml+xml[go_text/x-go[gpx_application/gpx+xml[gz_application/gzip[h_text/x-h[h261_video/h261[h263_video/h263[h264_video/h264[heic_image/heic[heics_image/heic-sequence[heif_image/heif[heifs_image/heif-sequence[htm,html,shtml_text/html[ico_image/x-icon[icns_image/x-icns[ics,ifb_text/calendar[iges,igs_model/iges[ink,inkml_application/inkml+xml[ipa_application/octet-stream[java_text/x-java-source[jp2,jpg2_image/jp2[jpeg,jpe,jpg_image/jpeg[jpf,jpx_image/jpx[jpm,jpgm_image/jpm[jpgv_video/jpeg[jph_image/jph[js,mjs_text/javascript[json_application/json[json5_application/json5[jsonld_application/ld+json[jsx_text/jsx[jxl_image/jxl[jxr_image/jxr[ktx_image/ktx[ktx2_image/ktx2[less_text/less[m1v,m2v,mpe,mpeg,mpg_video/mpeg[m4a_audio/mp4[m4v_video/x-m4v[md,markdown_text/markdown[mid,midi,kar,rmi_audio/midi[mkv_video/x-matroska[mp2,mp2a,mp3,mpga,m3a,m2a_audio/mpeg[mp4,mp4v,mpg4_video/mp4[mp4a_audio/mp4[mp4s,m4p_application/mp4[odp_application/vnd.oasis.opendocument.presentation[oda_application/oda[ods_application/vnd.oasis.opendocument.spreadsheet[odt_application/vnd.oasis.opendocument.text[oga,ogg,opus,spx_audio/ogg[ogv_video/ogg[ogx_application/ogg[otf_font/otf[p12,pfx_application/x-pkcs12[pdf_application/pdf[pem_application/x-pem-file[php_text/x-php[png_image/png[ppt_application/vnd.ms-powerpoint[pptx_application/vnd.openxmlformats-officedocument.presentationml.presentation[pskcxml_application/pskc+xml[psd_image/vnd.adobe.photoshop[py_text/x-python[qt,mov_video/quicktime[rar_application/vnd.rar[rdf_application/rdf+xml[rtf_text/rtf[sass_text/x-sass[scss_text/x-scss[sgm,sgml_text/sgml[sh_application/x-sh[svg,svgz_image/svg+xml[swf_application/x-shockwave-flash[tar_application/x-tar[tif,tiff_image/tiff[toml_application/toml[ts_video/mp2t[tsx_text/tsx[tsv_text/tab-separated-values[ttc_font/collection[ttf_font/ttf[vtt_text/vtt[wasm_application/wasm[wav_audio/wav[weba_audio/webm[webm_video/webm[webmanifest_application/manifest+json[webp_image/webp[wma_audio/x-ms-wma[wmv_video/x-ms-wmv[woff_font/woff[woff2_font/woff2[xls_application/vnd.ms-excel[xlsx_application/vnd.openxmlformats-officedocument.spreadsheetml.sheet[xml_application/xml[xz_application/x-xz[yaml,yml_text/yaml[zip_application/zip";
var mimeTypesMap = new Map(
mimeTypes.split("[").flatMap((mime) => {
const [extensions, mimeType] = mime.split("_");
return extensions.split(",").map((ext) => [ext, mimeType]);
})
);
// src/get-file-mime-type.ts
function getFileMimeType(name) {
const extension = name.split(".").pop();
return extension ? mimeTypesMap.get(extension) || null : null;
}
// src/is-valid-file-type.ts
function isFileAccepted(file, accept) {
if (file && accept) {
const types = Array.isArray(accept) ? accept : typeof accept === "string" ? accept.split(",") : [];
if (types.length === 0) return true;
const fileName = file.name || "";
const mimeType = (file.type || getFileMimeType(fileName) || "").toLowerCase();
const baseMimeType = mimeType.replace(/\/.*$/, "");
return types.some((type) => {
const validType = type.trim().toLowerCase();
if (validType.charAt(0) === ".") {
return fileName.toLowerCase().endsWith(validType);
}
if (validType.endsWith("/*")) {
return baseMimeType === validType.replace(/\/.*$/, "");
}
return mimeType === validType;
});
}
return true;
}
function isValidFileType(file, accept) {
const isAcceptable = file.type === "application/x-moz-file" || isFileAccepted(file, accept);
return [isAcceptable, isAcceptable ? null : "FILE_INVALID_TYPE"];
}
export { dataURItoBlob, downloadFile, getAcceptAttrString, getFileDataUrl, getFileEntries, getFileMimeType, getTotalFileSize, isFileEqual, isValidFileSize, isValidFileType };