@capgo/capacitor-file-picker 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CapgoCapacitorFilePicker.podspec +17 -0
- package/Package.swift +28 -0
- package/README.md +403 -0
- package/android/build.gradle +58 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/java/app/capgo/capacitor/filepicker/CapgoFilePickerPlugin.java +473 -0
- package/dist/docs.json +804 -0
- package/dist/esm/definitions.d.ts +347 -0
- package/dist/esm/definitions.js +2 -0
- package/dist/esm/definitions.js.map +1 -0
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.js +7 -0
- package/dist/esm/index.js.map +1 -0
- package/dist/esm/web.d.ts +22 -0
- package/dist/esm/web.js +147 -0
- package/dist/esm/web.js.map +1 -0
- package/dist/plugin.cjs.js +161 -0
- package/dist/plugin.cjs.js.map +1 -0
- package/dist/plugin.js +164 -0
- package/dist/plugin.js.map +1 -0
- package/ios/Sources/CapgoFilePickerPlugin/CapgoFilePickerPlugin.swift +393 -0
- package/ios/Tests/CapgoFilePickerPluginTests/CapgoFilePickerPluginTests.swift +11 -0
- package/package.json +88 -0
|
@@ -0,0 +1,393 @@
|
|
|
1
|
+
import AVFoundation
|
|
2
|
+
import Capacitor
|
|
3
|
+
import Foundation
|
|
4
|
+
import MobileCoreServices
|
|
5
|
+
import PhotosUI
|
|
6
|
+
import UniformTypeIdentifiers
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Please read the Capacitor iOS Plugin Development Guide
|
|
10
|
+
* here: https://capacitorjs.com/docs/plugins/ios
|
|
11
|
+
*/
|
|
12
|
+
@objc(CapgoFilePickerPlugin)
|
|
13
|
+
public class CapgoFilePickerPlugin: CAPPlugin, CAPBridgedPlugin {
|
|
14
|
+
private let pluginVersion: String = "8.0.0"
|
|
15
|
+
public let identifier = "CapgoFilePickerPlugin"
|
|
16
|
+
public let jsName = "CapgoFilePicker"
|
|
17
|
+
public let pluginMethods: [CAPPluginMethod] = [
|
|
18
|
+
CAPPluginMethod(name: "pickFiles", returnType: CAPPluginReturnPromise),
|
|
19
|
+
CAPPluginMethod(name: "pickImages", returnType: CAPPluginReturnPromise),
|
|
20
|
+
CAPPluginMethod(name: "pickVideos", returnType: CAPPluginReturnPromise),
|
|
21
|
+
CAPPluginMethod(name: "pickMedia", returnType: CAPPluginReturnPromise),
|
|
22
|
+
CAPPluginMethod(name: "pickDirectory", returnType: CAPPluginReturnPromise),
|
|
23
|
+
CAPPluginMethod(name: "convertHeicToJpeg", returnType: CAPPluginReturnPromise),
|
|
24
|
+
CAPPluginMethod(name: "copyFile", returnType: CAPPluginReturnPromise),
|
|
25
|
+
CAPPluginMethod(name: "checkPermissions", returnType: CAPPluginReturnPromise),
|
|
26
|
+
CAPPluginMethod(name: "requestPermissions", returnType: CAPPluginReturnPromise),
|
|
27
|
+
CAPPluginMethod(name: "getPluginVersion", returnType: CAPPluginReturnPromise)
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
private var pendingCall: CAPPluginCall?
|
|
31
|
+
private var readData: Bool = false
|
|
32
|
+
private var skipTranscoding: Bool = false
|
|
33
|
+
|
|
34
|
+
// MARK: - Pick Files
|
|
35
|
+
|
|
36
|
+
@objc func pickFiles(_ call: CAPPluginCall) {
|
|
37
|
+
let types = call.getArray("types", String.self) ?? []
|
|
38
|
+
let limit = call.getInt("limit") ?? 0
|
|
39
|
+
readData = call.getBool("readData") ?? false
|
|
40
|
+
|
|
41
|
+
DispatchQueue.main.async {
|
|
42
|
+
self.pendingCall = call
|
|
43
|
+
let documentTypes = self.convertToUTTypes(from: types)
|
|
44
|
+
let picker = UIDocumentPickerViewController(forOpeningContentTypes: documentTypes)
|
|
45
|
+
picker.delegate = self
|
|
46
|
+
picker.allowsMultipleSelection = limit != 1
|
|
47
|
+
self.bridge?.viewController?.present(picker, animated: true)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// MARK: - Pick Images
|
|
52
|
+
|
|
53
|
+
@objc func pickImages(_ call: CAPPluginCall) {
|
|
54
|
+
pickMedia(call, filter: .images)
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// MARK: - Pick Videos
|
|
58
|
+
|
|
59
|
+
@objc func pickVideos(_ call: CAPPluginCall) {
|
|
60
|
+
pickMedia(call, filter: .videos)
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// MARK: - Pick Media
|
|
64
|
+
|
|
65
|
+
@objc func pickMedia(_ call: CAPPluginCall) {
|
|
66
|
+
pickMedia(call, filter: nil)
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
private func pickMedia(_ call: CAPPluginCall, filter: PHPickerFilter?) {
|
|
70
|
+
let limit = call.getInt("limit") ?? 0
|
|
71
|
+
readData = call.getBool("readData") ?? false
|
|
72
|
+
skipTranscoding = call.getBool("skipTranscoding") ?? false
|
|
73
|
+
let ordered = call.getBool("ordered") ?? false
|
|
74
|
+
|
|
75
|
+
DispatchQueue.main.async {
|
|
76
|
+
self.pendingCall = call
|
|
77
|
+
var config = PHPickerConfiguration(photoLibrary: .shared())
|
|
78
|
+
config.selectionLimit = limit
|
|
79
|
+
if let filter = filter {
|
|
80
|
+
config.filter = filter
|
|
81
|
+
} else {
|
|
82
|
+
config.filter = .any(of: [.images, .videos])
|
|
83
|
+
}
|
|
84
|
+
if #available(iOS 15.0, *), ordered {
|
|
85
|
+
config.selection = .ordered
|
|
86
|
+
}
|
|
87
|
+
let picker = PHPickerViewController(configuration: config)
|
|
88
|
+
picker.delegate = self
|
|
89
|
+
self.bridge?.viewController?.present(picker, animated: true)
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// MARK: - Pick Directory
|
|
94
|
+
|
|
95
|
+
@objc func pickDirectory(_ call: CAPPluginCall) {
|
|
96
|
+
DispatchQueue.main.async {
|
|
97
|
+
self.pendingCall = call
|
|
98
|
+
let picker = UIDocumentPickerViewController(forOpeningContentTypes: [.folder])
|
|
99
|
+
picker.delegate = self
|
|
100
|
+
self.bridge?.viewController?.present(picker, animated: true)
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// MARK: - Convert HEIC to JPEG
|
|
105
|
+
|
|
106
|
+
@objc func convertHeicToJpeg(_ call: CAPPluginCall) {
|
|
107
|
+
guard let path = call.getString("path") else {
|
|
108
|
+
call.reject("path is required")
|
|
109
|
+
return
|
|
110
|
+
}
|
|
111
|
+
let quality = call.getFloat("quality") ?? 0.9
|
|
112
|
+
|
|
113
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
114
|
+
let url = URL(fileURLWithPath: path)
|
|
115
|
+
guard let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil),
|
|
116
|
+
let cgImage = CGImageSourceCreateImageAtIndex(imageSource, 0, nil) else {
|
|
117
|
+
call.reject("Failed to load image")
|
|
118
|
+
return
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
let uiImage = UIImage(cgImage: cgImage)
|
|
122
|
+
guard let jpegData = uiImage.jpegData(compressionQuality: CGFloat(quality)) else {
|
|
123
|
+
call.reject("Failed to convert to JPEG")
|
|
124
|
+
return
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
let outputPath = path.replacingOccurrences(of: ".heic", with: ".jpg", options: .caseInsensitive)
|
|
128
|
+
let outputURL = URL(fileURLWithPath: outputPath)
|
|
129
|
+
|
|
130
|
+
do {
|
|
131
|
+
try jpegData.write(to: outputURL)
|
|
132
|
+
call.resolve(["path": outputPath])
|
|
133
|
+
} catch {
|
|
134
|
+
call.reject("Failed to write JPEG: \(error.localizedDescription)")
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
// MARK: - Copy File
|
|
140
|
+
|
|
141
|
+
@objc func copyFile(_ call: CAPPluginCall) {
|
|
142
|
+
guard let from = call.getString("from"), let to = call.getString("to") else {
|
|
143
|
+
call.reject("from and to are required")
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
let overwrite = call.getBool("overwrite") ?? false
|
|
147
|
+
|
|
148
|
+
DispatchQueue.global(qos: .userInitiated).async {
|
|
149
|
+
let fileManager = FileManager.default
|
|
150
|
+
let fromURL = URL(fileURLWithPath: from)
|
|
151
|
+
let toURL = URL(fileURLWithPath: to)
|
|
152
|
+
|
|
153
|
+
do {
|
|
154
|
+
if fileManager.fileExists(atPath: to) {
|
|
155
|
+
if overwrite {
|
|
156
|
+
try fileManager.removeItem(at: toURL)
|
|
157
|
+
} else {
|
|
158
|
+
call.reject("Destination file already exists")
|
|
159
|
+
return
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
try fileManager.copyItem(at: fromURL, to: toURL)
|
|
163
|
+
call.resolve()
|
|
164
|
+
} catch {
|
|
165
|
+
call.reject("Failed to copy file: \(error.localizedDescription)")
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
// MARK: - Permissions
|
|
171
|
+
|
|
172
|
+
override public func checkPermissions(_ call: CAPPluginCall) {
|
|
173
|
+
let status = PHPhotoLibrary.authorizationStatus(for: .readWrite)
|
|
174
|
+
call.resolve(["readExternalStorage": permissionStateString(from: status)])
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
override public func requestPermissions(_ call: CAPPluginCall) {
|
|
178
|
+
PHPhotoLibrary.requestAuthorization(for: .readWrite) { status in
|
|
179
|
+
call.resolve(["readExternalStorage": self.permissionStateString(from: status)])
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
private func permissionStateString(from status: PHAuthorizationStatus) -> String {
|
|
184
|
+
switch status {
|
|
185
|
+
case .authorized, .limited:
|
|
186
|
+
return "granted"
|
|
187
|
+
case .denied, .restricted:
|
|
188
|
+
return "denied"
|
|
189
|
+
case .notDetermined:
|
|
190
|
+
return "prompt"
|
|
191
|
+
@unknown default:
|
|
192
|
+
return "prompt"
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// MARK: - Plugin Version
|
|
197
|
+
|
|
198
|
+
@objc func getPluginVersion(_ call: CAPPluginCall) {
|
|
199
|
+
call.resolve(["version": self.pluginVersion])
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// MARK: - Helpers
|
|
203
|
+
|
|
204
|
+
private func convertToUTTypes(from types: [String]) -> [UTType] {
|
|
205
|
+
if types.isEmpty {
|
|
206
|
+
return [.item]
|
|
207
|
+
}
|
|
208
|
+
var utTypes: [UTType] = []
|
|
209
|
+
for type in types {
|
|
210
|
+
if type == "*/*" {
|
|
211
|
+
return [.item]
|
|
212
|
+
} else if type.hasSuffix("/*") {
|
|
213
|
+
let prefix = String(type.dropLast(2))
|
|
214
|
+
switch prefix {
|
|
215
|
+
case "image":
|
|
216
|
+
utTypes.append(.image)
|
|
217
|
+
case "video":
|
|
218
|
+
utTypes.append(.movie)
|
|
219
|
+
case "audio":
|
|
220
|
+
utTypes.append(.audio)
|
|
221
|
+
case "text":
|
|
222
|
+
utTypes.append(.text)
|
|
223
|
+
default:
|
|
224
|
+
utTypes.append(.item)
|
|
225
|
+
}
|
|
226
|
+
} else if type.hasPrefix(".") {
|
|
227
|
+
if let utType = UTType(filenameExtension: String(type.dropFirst())) {
|
|
228
|
+
utTypes.append(utType)
|
|
229
|
+
}
|
|
230
|
+
} else if let utType = UTType(mimeType: type) {
|
|
231
|
+
utTypes.append(utType)
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return utTypes.isEmpty ? [.item] : utTypes
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
private func getFileInfo(from url: URL, readData: Bool) -> [String: Any] {
|
|
238
|
+
var result: [String: Any] = [
|
|
239
|
+
"name": url.lastPathComponent,
|
|
240
|
+
"path": url.path
|
|
241
|
+
]
|
|
242
|
+
|
|
243
|
+
if let mimeType = UTType(filenameExtension: url.pathExtension)?.preferredMIMEType {
|
|
244
|
+
result["mimeType"] = mimeType
|
|
245
|
+
} else {
|
|
246
|
+
result["mimeType"] = "application/octet-stream"
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
if let attributes = try? FileManager.default.attributesOfItem(atPath: url.path) {
|
|
250
|
+
if let size = attributes[.size] as? Int64 {
|
|
251
|
+
result["size"] = size
|
|
252
|
+
}
|
|
253
|
+
if let modifiedDate = attributes[.modificationDate] as? Date {
|
|
254
|
+
result["modifiedAt"] = Int64(modifiedDate.timeIntervalSince1970 * 1000)
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if readData, let data = try? Data(contentsOf: url) {
|
|
259
|
+
result["data"] = data.base64EncodedString()
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
// Get dimensions for images
|
|
263
|
+
if let mimeType = result["mimeType"] as? String, mimeType.hasPrefix("image/") {
|
|
264
|
+
if let imageSource = CGImageSourceCreateWithURL(url as CFURL, nil),
|
|
265
|
+
let properties = CGImageSourceCopyPropertiesAtIndex(imageSource, 0, nil) as? [String: Any] {
|
|
266
|
+
if let width = properties[kCGImagePropertyPixelWidth as String] as? Int {
|
|
267
|
+
result["width"] = width
|
|
268
|
+
}
|
|
269
|
+
if let height = properties[kCGImagePropertyPixelHeight as String] as? Int {
|
|
270
|
+
result["height"] = height
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return result
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
// MARK: - UIDocumentPickerDelegate
|
|
280
|
+
|
|
281
|
+
extension CapgoFilePickerPlugin: UIDocumentPickerDelegate {
|
|
282
|
+
public func documentPicker(_ controller: UIDocumentPickerViewController, didPickDocumentsAt urls: [URL]) {
|
|
283
|
+
guard let call = pendingCall else { return }
|
|
284
|
+
pendingCall = nil
|
|
285
|
+
|
|
286
|
+
// Check if this was a directory picker
|
|
287
|
+
if urls.count == 1, urls[0].hasDirectoryPath {
|
|
288
|
+
let granted = urls[0].startAccessingSecurityScopedResource()
|
|
289
|
+
if granted {
|
|
290
|
+
call.resolve(["path": urls[0].path])
|
|
291
|
+
urls[0].stopAccessingSecurityScopedResource()
|
|
292
|
+
} else {
|
|
293
|
+
call.resolve(["path": urls[0].path])
|
|
294
|
+
}
|
|
295
|
+
return
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
var files: [[String: Any]] = []
|
|
299
|
+
for url in urls {
|
|
300
|
+
let granted = url.startAccessingSecurityScopedResource()
|
|
301
|
+
defer { if granted { url.stopAccessingSecurityScopedResource() } }
|
|
302
|
+
files.append(getFileInfo(from: url, readData: readData))
|
|
303
|
+
}
|
|
304
|
+
call.resolve(["files": files])
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
public func documentPickerWasCancelled(_ controller: UIDocumentPickerViewController) {
|
|
308
|
+
guard let call = pendingCall else { return }
|
|
309
|
+
pendingCall = nil
|
|
310
|
+
notifyListeners("pickerDismissed", data: [:])
|
|
311
|
+
call.resolve(["files": []])
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// MARK: - PHPickerViewControllerDelegate
|
|
316
|
+
|
|
317
|
+
extension CapgoFilePickerPlugin: PHPickerViewControllerDelegate {
|
|
318
|
+
public func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
|
319
|
+
picker.dismiss(animated: true)
|
|
320
|
+
|
|
321
|
+
guard let call = pendingCall else { return }
|
|
322
|
+
|
|
323
|
+
if results.isEmpty {
|
|
324
|
+
pendingCall = nil
|
|
325
|
+
notifyListeners("pickerDismissed", data: [:])
|
|
326
|
+
call.resolve(["files": []])
|
|
327
|
+
return
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
let group = DispatchGroup()
|
|
331
|
+
var files: [[String: Any]] = []
|
|
332
|
+
let filesLock = NSLock()
|
|
333
|
+
|
|
334
|
+
for result in results {
|
|
335
|
+
group.enter()
|
|
336
|
+
|
|
337
|
+
let itemProvider = result.itemProvider
|
|
338
|
+
|
|
339
|
+
// Determine the UTType to load
|
|
340
|
+
let typeIdentifier: String
|
|
341
|
+
if itemProvider.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
|
|
342
|
+
typeIdentifier = UTType.movie.identifier
|
|
343
|
+
} else if itemProvider.hasItemConformingToTypeIdentifier(UTType.image.identifier) {
|
|
344
|
+
typeIdentifier = UTType.image.identifier
|
|
345
|
+
} else {
|
|
346
|
+
typeIdentifier = itemProvider.registeredTypeIdentifiers.first ?? UTType.data.identifier
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
itemProvider.loadFileRepresentation(forTypeIdentifier: typeIdentifier) { [weak self] url, _ in
|
|
350
|
+
guard let self = self, let url = url else {
|
|
351
|
+
group.leave()
|
|
352
|
+
return
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
// Copy to temp directory to keep the file accessible
|
|
356
|
+
let tempDir = FileManager.default.temporaryDirectory
|
|
357
|
+
let tempURL = tempDir.appendingPathComponent(UUID().uuidString + "_" + url.lastPathComponent)
|
|
358
|
+
|
|
359
|
+
do {
|
|
360
|
+
try FileManager.default.copyItem(at: url, to: tempURL)
|
|
361
|
+
var fileInfo = self.getFileInfo(from: tempURL, readData: self.readData)
|
|
362
|
+
|
|
363
|
+
// Get video duration if applicable
|
|
364
|
+
if let mimeType = fileInfo["mimeType"] as? String, mimeType.hasPrefix("video/") {
|
|
365
|
+
let asset = AVURLAsset(url: tempURL)
|
|
366
|
+
let duration = CMTimeGetSeconds(asset.duration)
|
|
367
|
+
if duration.isFinite {
|
|
368
|
+
fileInfo["duration"] = duration
|
|
369
|
+
}
|
|
370
|
+
// Get video dimensions
|
|
371
|
+
if let track = asset.tracks(withMediaType: .video).first {
|
|
372
|
+
let size = track.naturalSize.applying(track.preferredTransform)
|
|
373
|
+
fileInfo["width"] = abs(Int(size.width))
|
|
374
|
+
fileInfo["height"] = abs(Int(size.height))
|
|
375
|
+
}
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
filesLock.lock()
|
|
379
|
+
files.append(fileInfo)
|
|
380
|
+
filesLock.unlock()
|
|
381
|
+
} catch {
|
|
382
|
+
// Silently fail for individual files
|
|
383
|
+
}
|
|
384
|
+
group.leave()
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
group.notify(queue: .main) { [weak self] in
|
|
389
|
+
self?.pendingCall = nil
|
|
390
|
+
call.resolve(["files": files])
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import XCTest
|
|
2
|
+
@testable import CapgoFilePickerPlugin
|
|
3
|
+
|
|
4
|
+
final class CapgoFilePickerPluginTests: XCTestCase {
|
|
5
|
+
func testPluginLoads() throws {
|
|
6
|
+
let plugin = CapgoFilePickerPlugin()
|
|
7
|
+
XCTAssertNotNil(plugin)
|
|
8
|
+
XCTAssertEqual(plugin.identifier, "CapgoFilePickerPlugin")
|
|
9
|
+
XCTAssertEqual(plugin.jsName, "CapgoFilePicker")
|
|
10
|
+
}
|
|
11
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@capgo/capacitor-file-picker",
|
|
3
|
+
"version": "8.0.0",
|
|
4
|
+
"description": "File picker Capacitor plugin - Pick files, images, videos, and directories",
|
|
5
|
+
"main": "dist/plugin.cjs.js",
|
|
6
|
+
"module": "dist/esm/index.js",
|
|
7
|
+
"types": "dist/esm/index.d.ts",
|
|
8
|
+
"unpkg": "dist/plugin.js",
|
|
9
|
+
"files": [
|
|
10
|
+
"android/src/main/",
|
|
11
|
+
"android/build.gradle",
|
|
12
|
+
"dist/",
|
|
13
|
+
"ios/Sources",
|
|
14
|
+
"ios/Tests",
|
|
15
|
+
"Package.swift",
|
|
16
|
+
"CapgoCapacitorFilePicker.podspec"
|
|
17
|
+
],
|
|
18
|
+
"author": "Martin Donadieu <martin@capgo.app>",
|
|
19
|
+
"license": "MPL-2.0",
|
|
20
|
+
"repository": {
|
|
21
|
+
"type": "git",
|
|
22
|
+
"url": "git+https://github.com/Cap-go/capacitor-file-picker.git"
|
|
23
|
+
},
|
|
24
|
+
"bugs": {
|
|
25
|
+
"url": "https://github.com/Cap-go/capacitor-file-picker/issues"
|
|
26
|
+
},
|
|
27
|
+
"keywords": [
|
|
28
|
+
"capacitor",
|
|
29
|
+
"plugin",
|
|
30
|
+
"native",
|
|
31
|
+
"file",
|
|
32
|
+
"picker",
|
|
33
|
+
"image",
|
|
34
|
+
"video",
|
|
35
|
+
"media",
|
|
36
|
+
"gallery"
|
|
37
|
+
],
|
|
38
|
+
"scripts": {
|
|
39
|
+
"verify": "npm run verify:ios && npm run verify:android && npm run verify:web",
|
|
40
|
+
"verify:ios": "xcodebuild -scheme CapgoCapacitorFilePicker -destination generic/platform=iOS",
|
|
41
|
+
"verify:android": "cd android && ./gradlew clean build test && cd ..",
|
|
42
|
+
"verify:web": "npm run build",
|
|
43
|
+
"lint": "npm run eslint && npm run prettier -- --check && npm run swiftlint -- lint",
|
|
44
|
+
"fmt": "npm run eslint -- --fix && npm run prettier -- --write && npm run swiftlint -- --fix --format",
|
|
45
|
+
"eslint": "eslint . --ext ts",
|
|
46
|
+
"prettier": "prettier \"**/*.{css,html,ts,js,java}\" --plugin=prettier-plugin-java",
|
|
47
|
+
"swiftlint": "node-swiftlint",
|
|
48
|
+
"docgen": "docgen --api CapgoFilePickerPlugin --output-readme README.md --output-json dist/docs.json",
|
|
49
|
+
"build": "npm run clean && npm run docgen && tsc && rollup -c rollup.config.mjs",
|
|
50
|
+
"clean": "rimraf ./dist",
|
|
51
|
+
"watch": "tsc --watch",
|
|
52
|
+
"prepublishOnly": "npm run build"
|
|
53
|
+
},
|
|
54
|
+
"devDependencies": {
|
|
55
|
+
"@capacitor/android": "^8.0.0",
|
|
56
|
+
"@capacitor/core": "^8.0.0",
|
|
57
|
+
"@capacitor/docgen": "^0.3.1",
|
|
58
|
+
"@capacitor/ios": "^8.0.0",
|
|
59
|
+
"@ionic/eslint-config": "^0.4.0",
|
|
60
|
+
"@ionic/prettier-config": "^4.0.0",
|
|
61
|
+
"@ionic/swiftlint-config": "^2.0.0",
|
|
62
|
+
"@types/node": "^24.10.1",
|
|
63
|
+
"eslint": "^8.57.1",
|
|
64
|
+
"eslint-plugin-import": "^2.31.0",
|
|
65
|
+
"prettier": "^3.6.2",
|
|
66
|
+
"prettier-plugin-java": "^2.7.7",
|
|
67
|
+
"rimraf": "^6.1.0",
|
|
68
|
+
"rollup": "^4.53.2",
|
|
69
|
+
"swiftlint": "^2.0.0",
|
|
70
|
+
"typescript": "^5.9.3"
|
|
71
|
+
},
|
|
72
|
+
"peerDependencies": {
|
|
73
|
+
"@capacitor/core": ">=8.0.0"
|
|
74
|
+
},
|
|
75
|
+
"prettier": "@ionic/prettier-config",
|
|
76
|
+
"swiftlint": "@ionic/swiftlint-config",
|
|
77
|
+
"eslintConfig": {
|
|
78
|
+
"extends": "@ionic/eslint-config/recommended"
|
|
79
|
+
},
|
|
80
|
+
"capacitor": {
|
|
81
|
+
"ios": {
|
|
82
|
+
"src": "ios"
|
|
83
|
+
},
|
|
84
|
+
"android": {
|
|
85
|
+
"src": "android"
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|