Skip to content

Commit 191b5b7

Browse files
committed
Migrate dependencies that do not support esm
1 parent 4d62e29 commit 191b5b7

File tree

10 files changed

+369
-24
lines changed

10 files changed

+369
-24
lines changed

package.json

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -54,8 +54,6 @@
5454
"react": ">= 16.8 || 18.0.0"
5555
},
5656
"dependencies": {
57-
"attr-accept": "^2.2.2",
58-
"file-selector": "^0.6.0",
5957
"prop-types": "^15.8.1"
6058
},
6159
"devDependencies": {

rollup.config.mjs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@ export default {
2323
],
2424

2525
plugins: [
26-
esbuild({ minify: true, sourceMap: false, target: "es2015" }),
26+
esbuild({ minify: false, sourceMap: false, target: "es2015" }),
2727
nodeExternals(),
2828
nodeResolve(),
2929
commonjs(),

src/attr-accept.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
/**
2+
* Check if the provided file type should be accepted by the input with accept attribute.
3+
* https://developer.mozilla.org/en-US/docs/Web/HTML/Element/Input#attr-accept
4+
*
5+
* Inspired by https://github.com/enyo/dropzone
6+
*
7+
* @param file {File} https://developer.mozilla.org/en-US/docs/Web/API/File
8+
* @param acceptedFiles {string}
9+
* @returns {boolean}
10+
*/
11+
12+
export default function (file, acceptedFiles) {
13+
if (file && acceptedFiles) {
14+
const acceptedFilesArray = Array.isArray(acceptedFiles)
15+
? acceptedFiles
16+
: acceptedFiles.split(",");
17+
const fileName = file.name || "";
18+
const mimeType = (file.type || "").toLowerCase();
19+
const baseMimeType = mimeType.replace(/\/.*$/, "");
20+
21+
return acceptedFilesArray.some((type) => {
22+
const validType = type.trim().toLowerCase();
23+
if (validType.charAt(0) === ".") {
24+
return fileName.toLowerCase().endsWith(validType);
25+
} else if (validType.endsWith("/*")) {
26+
// This is something like a image/* mime type
27+
return baseMimeType === validType.replace(/\/.*$/, "");
28+
}
29+
return mimeType === validType;
30+
});
31+
}
32+
return true;
33+
}

src/file-selector.js

Lines changed: 189 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,189 @@
1+
import { toFileWithPath } from "./file";
2+
3+
const FILES_TO_IGNORE = [
4+
// Thumbnail cache files for macOS and Windows
5+
".DS_Store", // macOs
6+
"Thumbs.db", // Windows
7+
];
8+
9+
/**
10+
* Convert a DragEvent's DataTrasfer object to a list of File objects
11+
* NOTE: If some of the items are folders,
12+
* everything will be flattened and placed in the same list but the paths will be kept as a {path} property.
13+
*
14+
* EXPERIMENTAL: A list of https://developer.mozilla.org/en-US/docs/Web/API/FileSystemHandle objects can also be passed as an arg
15+
* and a list of File objects will be returned.
16+
*
17+
* @param evt
18+
*/
19+
export async function fromEvent(evt) {
20+
if (isObject(evt) && isDataTransfer(evt.dataTransfer)) {
21+
return getDataTransferFiles(evt.dataTransfer, evt.type);
22+
} else if (isChangeEvt(evt)) {
23+
return getInputFiles(evt);
24+
} else if (
25+
Array.isArray(evt) &&
26+
evt.every((item) => "getFile" in item && typeof item.getFile === "function")
27+
) {
28+
return getFsHandleFiles(evt);
29+
}
30+
return [];
31+
}
32+
33+
function isDataTransfer(value) {
34+
return isObject(value);
35+
}
36+
37+
function isChangeEvt(value) {
38+
return isObject(value) && isObject(value.target);
39+
}
40+
41+
function isObject(v) {
42+
return typeof v === "object" && v !== null;
43+
}
44+
45+
function getInputFiles(evt) {
46+
return fromList(evt.target.files).map((file) => toFileWithPath(file));
47+
}
48+
49+
// Ee expect each handle to be https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileHandle
50+
async function getFsHandleFiles(handles) {
51+
const files = await Promise.all(handles.map((h) => h.getFile()));
52+
return files.map((file) => toFileWithPath(file));
53+
}
54+
55+
async function getDataTransferFiles(dt, type) {
56+
// IE11 does not support dataTransfer.items
57+
// See https://developer.mozilla.org/en-US/docs/Web/API/DataTransfer/items#Browser_compatibility
58+
if (dt.items) {
59+
const items = fromList(dt.items).filter((item) => item.kind === "file");
60+
// According to https://html.spec.whatwg.org/multipage/dnd.html#dndevents,
61+
// only 'dragstart' and 'drop' has access to the data (source node)
62+
if (type !== "drop") {
63+
return items;
64+
}
65+
const files = await Promise.all(items.map(toFilePromises));
66+
return noIgnoredFiles(flatten(files));
67+
}
68+
69+
return noIgnoredFiles(fromList(dt.files).map((file) => toFileWithPath(file)));
70+
}
71+
72+
function noIgnoredFiles(files) {
73+
return files.filter((file) => FILES_TO_IGNORE.indexOf(file.name) === -1);
74+
}
75+
76+
// IE11 does not support Array.from()
77+
// https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/from#Browser_compatibility
78+
// https://developer.mozilla.org/en-US/docs/Web/API/FileList
79+
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItemList
80+
function fromList(items) {
81+
if (items === null) {
82+
return [];
83+
}
84+
85+
const files = [];
86+
87+
// tslint:disable: prefer-for-of
88+
for (let i = 0; i < items.length; i++) {
89+
const file = items[i];
90+
files.push(file);
91+
}
92+
93+
return files;
94+
}
95+
96+
// https://developer.mozilla.org/en-US/docs/Web/API/DataTransferItem
97+
function toFilePromises(item) {
98+
if (typeof item.webkitGetAsEntry !== "function") {
99+
return fromDataTransferItem(item);
100+
}
101+
102+
const entry = item.webkitGetAsEntry();
103+
104+
// Safari supports dropping an image node from a different window and can be retrieved using
105+
// the DataTransferItem.getAsFile() API
106+
// NOTE: FileSystemEntry.file() throws if trying to get the file
107+
if (entry && entry.isDirectory) {
108+
return fromDirEntry(entry);
109+
}
110+
111+
return fromDataTransferItem(item);
112+
}
113+
114+
function flatten(items) {
115+
return items.reduce(
116+
(acc, files) => [
117+
...acc,
118+
...(Array.isArray(files) ? flatten(files) : [files]),
119+
],
120+
[]
121+
);
122+
}
123+
124+
function fromDataTransferItem(item) {
125+
const file = item.getAsFile();
126+
if (!file) {
127+
return Promise.reject(`${item} is not a File`);
128+
}
129+
const fwp = toFileWithPath(file);
130+
return Promise.resolve(fwp);
131+
}
132+
133+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemEntry
134+
async function fromEntry(entry) {
135+
return entry.isDirectory ? fromDirEntry(entry) : fromFileEntry(entry);
136+
}
137+
138+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry
139+
function fromDirEntry(entry) {
140+
const reader = entry.createReader();
141+
142+
return new Promise((resolve, reject) => {
143+
const entries = [];
144+
145+
function readEntries() {
146+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryEntry/createReader
147+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemDirectoryReader/readEntries
148+
reader.readEntries(
149+
async (batch) => {
150+
if (!batch.length) {
151+
// Done reading directory
152+
try {
153+
const files = await Promise.all(entries);
154+
resolve(files);
155+
} catch (err) {
156+
reject(err);
157+
}
158+
} else {
159+
const items = Promise.all(batch.map(fromEntry));
160+
entries.push(items);
161+
162+
// Continue reading
163+
readEntries();
164+
}
165+
},
166+
(err) => {
167+
reject(err);
168+
}
169+
);
170+
}
171+
172+
readEntries();
173+
});
174+
}
175+
176+
// https://developer.mozilla.org/en-US/docs/Web/API/FileSystemFileEntry
177+
async function fromFileEntry(entry) {
178+
return new Promise((resolve, reject) => {
179+
entry.file(
180+
(file) => {
181+
const fwp = toFileWithPath(file, entry.fullPath);
182+
resolve(fwp);
183+
},
184+
(err) => {
185+
reject(err);
186+
}
187+
);
188+
});
189+
}

src/file.js

Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
export const COMMON_MIME_TYPES = new Map([
2+
// https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types/Common_types
3+
["aac", "audio/aac"],
4+
["abw", "application/x-abiword"],
5+
["arc", "application/x-freearc"],
6+
["avif", "image/avif"],
7+
["avi", "video/x-msvideo"],
8+
["azw", "application/vnd.amazon.ebook"],
9+
["bin", "application/octet-stream"],
10+
["bmp", "image/bmp"],
11+
["bz", "application/x-bzip"],
12+
["bz2", "application/x-bzip2"],
13+
["cda", "application/x-cdf"],
14+
["csh", "application/x-csh"],
15+
["css", "text/css"],
16+
["csv", "text/csv"],
17+
["doc", "application/msword"],
18+
[
19+
"docx",
20+
"application/vnd.openxmlformats-officedocument.wordprocessingml.document",
21+
],
22+
["eot", "application/vnd.ms-fontobject"],
23+
["epub", "application/epub+zip"],
24+
["gz", "application/gzip"],
25+
["gif", "image/gif"],
26+
["heic", "image/heic"],
27+
["heif", "image/heif"],
28+
["htm", "text/html"],
29+
["html", "text/html"],
30+
["ico", "image/vnd.microsoft.icon"],
31+
["ics", "text/calendar"],
32+
["jar", "application/java-archive"],
33+
["jpeg", "image/jpeg"],
34+
["jpg", "image/jpeg"],
35+
["js", "text/javascript"],
36+
["json", "application/json"],
37+
["jsonld", "application/ld+json"],
38+
["mid", "audio/midi"],
39+
["midi", "audio/midi"],
40+
["mjs", "text/javascript"],
41+
["mp3", "audio/mpeg"],
42+
["mp4", "video/mp4"],
43+
["mpeg", "video/mpeg"],
44+
["mpkg", "application/vnd.apple.installer+xml"],
45+
["odp", "application/vnd.oasis.opendocument.presentation"],
46+
["ods", "application/vnd.oasis.opendocument.spreadsheet"],
47+
["odt", "application/vnd.oasis.opendocument.text"],
48+
["oga", "audio/ogg"],
49+
["ogv", "video/ogg"],
50+
["ogx", "application/ogg"],
51+
["opus", "audio/opus"],
52+
["otf", "font/otf"],
53+
["png", "image/png"],
54+
["pdf", "application/pdf"],
55+
["php", "application/x-httpd-php"],
56+
["ppt", "application/vnd.ms-powerpoint"],
57+
[
58+
"pptx",
59+
"application/vnd.openxmlformats-officedocument.presentationml.presentation",
60+
],
61+
["rar", "application/vnd.rar"],
62+
["rtf", "application/rtf"],
63+
["sh", "application/x-sh"],
64+
["svg", "image/svg+xml"],
65+
["swf", "application/x-shockwave-flash"],
66+
["tar", "application/x-tar"],
67+
["tif", "image/tiff"],
68+
["tiff", "image/tiff"],
69+
["ts", "video/mp2t"],
70+
["ttf", "font/ttf"],
71+
["txt", "text/plain"],
72+
["vsd", "application/vnd.visio"],
73+
["wav", "audio/wav"],
74+
["weba", "audio/webm"],
75+
["webm", "video/webm"],
76+
["webp", "image/webp"],
77+
["woff", "font/woff"],
78+
["woff2", "font/woff2"],
79+
["xhtml", "application/xhtml+xml"],
80+
["xls", "application/vnd.ms-excel"],
81+
["xlsx", "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"],
82+
["xml", "application/xml"],
83+
["xul", "application/vnd.mozilla.xul+xml"],
84+
["zip", "application/zip"],
85+
["7z", "application/x-7z-compressed"],
86+
87+
// Others
88+
["mkv", "video/x-matroska"],
89+
["mov", "video/quicktime"],
90+
["msg", "application/vnd.ms-outlook"],
91+
]);
92+
93+
export function toFileWithPath(file, path) {
94+
const f = withMimeType(file);
95+
if (typeof f.path !== "string") {
96+
// on electron, path is already set to the absolute path
97+
const { webkitRelativePath } = file;
98+
Object.defineProperty(f, "path", {
99+
value:
100+
typeof path === "string"
101+
? path
102+
: // If <input webkitdirectory> is set,
103+
// the File will have a {webkitRelativePath} property
104+
// https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement/webkitdirectory
105+
typeof webkitRelativePath === "string" &&
106+
webkitRelativePath.length > 0
107+
? webkitRelativePath
108+
: file.name,
109+
writable: false,
110+
configurable: false,
111+
enumerable: true,
112+
});
113+
}
114+
115+
return f;
116+
}
117+
118+
function withMimeType(file) {
119+
const { name } = file;
120+
const hasExtension = name && name.lastIndexOf(".") !== -1;
121+
122+
if (hasExtension && !file.type) {
123+
const ext = name.split(".").pop().toLowerCase();
124+
const type = COMMON_MIME_TYPES.get(ext);
125+
if (type) {
126+
Object.defineProperty(file, "type", {
127+
value: type,
128+
writable: false,
129+
configurable: false,
130+
enumerable: true,
131+
});
132+
}
133+
}
134+
135+
return file;
136+
}

src/index.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import React, {
1010
useRef,
1111
} from "react";
1212
import PropTypes from "prop-types";
13-
import { fromEvent } from "file-selector";
13+
import { fromEvent } from "./file-selector";
1414
import {
1515
acceptPropAsAcceptAttr,
1616
allFilesAccepted,

src/utils.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import accepts from "attr-accept";
1+
import accepts from "./attr-accept";
22

33
// Error codes
44
export const FILE_INVALID_TYPE = "file-invalid-type";

0 commit comments

Comments
 (0)