fs.cjs 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416
  1. 'use strict';
  2. var tauri = require('./helpers/tauri.cjs');
  3. // Copyright 2019-2023 Tauri Programme within The Commons Conservancy
  4. // SPDX-License-Identifier: Apache-2.0
  5. // SPDX-License-Identifier: MIT
  6. /**
  7. * Access the file system.
  8. *
  9. * This package is also accessible with `window.__TAURI__.fs` when [`build.withGlobalTauri`](https://tauri.app/v1/api/config/#buildconfig.withglobaltauri) in `tauri.conf.json` is set to `true`.
  10. *
  11. * The APIs must be added to [`tauri.allowlist.fs`](https://tauri.app/v1/api/config/#allowlistconfig.fs) in `tauri.conf.json`:
  12. * ```json
  13. * {
  14. * "tauri": {
  15. * "allowlist": {
  16. * "fs": {
  17. * "all": true, // enable all FS APIs
  18. * "readFile": true,
  19. * "writeFile": true,
  20. * "readDir": true,
  21. * "copyFile": true,
  22. * "createDir": true,
  23. * "removeDir": true,
  24. * "removeFile": true,
  25. * "renameFile": true,
  26. * "exists": true
  27. * }
  28. * }
  29. * }
  30. * }
  31. * ```
  32. * It is recommended to allowlist only the APIs you use for optimal bundle size and security.
  33. *
  34. * ## Security
  35. *
  36. * This module prevents path traversal, not allowing absolute paths or parent dir components
  37. * (i.e. "/usr/path/to/file" or "../path/to/file" paths are not allowed).
  38. * Paths accessed with this API must be relative to one of the {@link BaseDirectory | base directories}
  39. * so if you need access to arbitrary filesystem paths, you must write such logic on the core layer instead.
  40. *
  41. * The API has a scope configuration that forces you to restrict the paths that can be accessed using glob patterns.
  42. *
  43. * The scope configuration is an array of glob patterns describing folder paths that are allowed.
  44. * For instance, this scope configuration only allows accessing files on the
  45. * *databases* folder of the {@link path.appDataDir | $APPDATA directory}:
  46. * ```json
  47. * {
  48. * "tauri": {
  49. * "allowlist": {
  50. * "fs": {
  51. * "scope": ["$APPDATA/databases/*"]
  52. * }
  53. * }
  54. * }
  55. * }
  56. * ```
  57. *
  58. * Notice the use of the `$APPDATA` variable. The value is injected at runtime, resolving to the {@link path.appDataDir | app data directory}.
  59. * The available variables are:
  60. * {@link path.appConfigDir | `$APPCONFIG`}, {@link path.appDataDir | `$APPDATA`}, {@link path.appLocalDataDir | `$APPLOCALDATA`},
  61. * {@link path.appCacheDir | `$APPCACHE`}, {@link path.appLogDir | `$APPLOG`},
  62. * {@link path.audioDir | `$AUDIO`}, {@link path.cacheDir | `$CACHE`}, {@link path.configDir | `$CONFIG`}, {@link path.dataDir | `$DATA`},
  63. * {@link path.localDataDir | `$LOCALDATA`}, {@link path.desktopDir | `$DESKTOP`}, {@link path.documentDir | `$DOCUMENT`},
  64. * {@link path.downloadDir | `$DOWNLOAD`}, {@link path.executableDir | `$EXE`}, {@link path.fontDir | `$FONT`}, {@link path.homeDir | `$HOME`},
  65. * {@link path.pictureDir | `$PICTURE`}, {@link path.publicDir | `$PUBLIC`}, {@link path.runtimeDir | `$RUNTIME`},
  66. * {@link path.templateDir | `$TEMPLATE`}, {@link path.videoDir | `$VIDEO`}, {@link path.resourceDir | `$RESOURCE`}, {@link path.appDir | `$APP`},
  67. * {@link path.logDir | `$LOG`}, {@link os.tempdir | `$TEMP`}.
  68. *
  69. * Trying to execute any API with a URL not configured on the scope results in a promise rejection due to denied access.
  70. *
  71. * Note that this scope applies to **all** APIs on this module.
  72. *
  73. * @module
  74. */
  75. /**
  76. * @since 1.0.0
  77. */
  78. exports.Dir = void 0;
  79. (function (BaseDirectory) {
  80. BaseDirectory[BaseDirectory["Audio"] = 1] = "Audio";
  81. BaseDirectory[BaseDirectory["Cache"] = 2] = "Cache";
  82. BaseDirectory[BaseDirectory["Config"] = 3] = "Config";
  83. BaseDirectory[BaseDirectory["Data"] = 4] = "Data";
  84. BaseDirectory[BaseDirectory["LocalData"] = 5] = "LocalData";
  85. BaseDirectory[BaseDirectory["Desktop"] = 6] = "Desktop";
  86. BaseDirectory[BaseDirectory["Document"] = 7] = "Document";
  87. BaseDirectory[BaseDirectory["Download"] = 8] = "Download";
  88. BaseDirectory[BaseDirectory["Executable"] = 9] = "Executable";
  89. BaseDirectory[BaseDirectory["Font"] = 10] = "Font";
  90. BaseDirectory[BaseDirectory["Home"] = 11] = "Home";
  91. BaseDirectory[BaseDirectory["Picture"] = 12] = "Picture";
  92. BaseDirectory[BaseDirectory["Public"] = 13] = "Public";
  93. BaseDirectory[BaseDirectory["Runtime"] = 14] = "Runtime";
  94. BaseDirectory[BaseDirectory["Template"] = 15] = "Template";
  95. BaseDirectory[BaseDirectory["Video"] = 16] = "Video";
  96. BaseDirectory[BaseDirectory["Resource"] = 17] = "Resource";
  97. BaseDirectory[BaseDirectory["App"] = 18] = "App";
  98. BaseDirectory[BaseDirectory["Log"] = 19] = "Log";
  99. BaseDirectory[BaseDirectory["Temp"] = 20] = "Temp";
  100. BaseDirectory[BaseDirectory["AppConfig"] = 21] = "AppConfig";
  101. BaseDirectory[BaseDirectory["AppData"] = 22] = "AppData";
  102. BaseDirectory[BaseDirectory["AppLocalData"] = 23] = "AppLocalData";
  103. BaseDirectory[BaseDirectory["AppCache"] = 24] = "AppCache";
  104. BaseDirectory[BaseDirectory["AppLog"] = 25] = "AppLog";
  105. })(exports.Dir || (exports.Dir = {}));
  106. /**
  107. * Reads a file as an UTF-8 encoded string.
  108. * @example
  109. * ```typescript
  110. * import { readTextFile, BaseDirectory } from '@tauri-apps/api/fs';
  111. * // Read the text file in the `$APPCONFIG/app.conf` path
  112. * const contents = await readTextFile('app.conf', { dir: BaseDirectory.AppConfig });
  113. * ```
  114. *
  115. * @since 1.0.0
  116. */
  117. async function readTextFile(filePath, options = {}) {
  118. return tauri.invokeTauriCommand({
  119. __tauriModule: 'Fs',
  120. message: {
  121. cmd: 'readTextFile',
  122. path: filePath,
  123. options
  124. }
  125. });
  126. }
  127. /**
  128. * Reads a file as byte array.
  129. * @example
  130. * ```typescript
  131. * import { readBinaryFile, BaseDirectory } from '@tauri-apps/api/fs';
  132. * // Read the image file in the `$RESOURCEDIR/avatar.png` path
  133. * const contents = await readBinaryFile('avatar.png', { dir: BaseDirectory.Resource });
  134. * ```
  135. *
  136. * @since 1.0.0
  137. */
  138. async function readBinaryFile(filePath, options = {}) {
  139. const arr = await tauri.invokeTauriCommand({
  140. __tauriModule: 'Fs',
  141. message: {
  142. cmd: 'readFile',
  143. path: filePath,
  144. options
  145. }
  146. });
  147. return Uint8Array.from(arr);
  148. }
  149. /**
  150. * Writes a UTF-8 text file.
  151. *
  152. * @returns A promise indicating the success or failure of the operation.
  153. *
  154. * @since 1.0.0
  155. */
  156. async function writeTextFile(path, contents, options) {
  157. if (typeof options === 'object') {
  158. Object.freeze(options);
  159. }
  160. if (typeof path === 'object') {
  161. Object.freeze(path);
  162. }
  163. const file = { path: '', contents: '' };
  164. let fileOptions = options;
  165. if (typeof path === 'string') {
  166. file.path = path;
  167. }
  168. else {
  169. file.path = path.path;
  170. file.contents = path.contents;
  171. }
  172. if (typeof contents === 'string') {
  173. file.contents = contents !== null && contents !== void 0 ? contents : '';
  174. }
  175. else {
  176. fileOptions = contents;
  177. }
  178. return tauri.invokeTauriCommand({
  179. __tauriModule: 'Fs',
  180. message: {
  181. cmd: 'writeFile',
  182. path: file.path,
  183. contents: Array.from(new TextEncoder().encode(file.contents)),
  184. options: fileOptions
  185. }
  186. });
  187. }
  188. /**
  189. * Writes a byte array content to a file.
  190. *
  191. * @returns A promise indicating the success or failure of the operation.
  192. *
  193. * @since 1.0.0
  194. */
  195. async function writeBinaryFile(path, contents, options) {
  196. if (typeof options === 'object') {
  197. Object.freeze(options);
  198. }
  199. if (typeof path === 'object') {
  200. Object.freeze(path);
  201. }
  202. const file = { path: '', contents: [] };
  203. let fileOptions = options;
  204. if (typeof path === 'string') {
  205. file.path = path;
  206. }
  207. else {
  208. file.path = path.path;
  209. file.contents = path.contents;
  210. }
  211. if (contents && 'dir' in contents) {
  212. fileOptions = contents;
  213. }
  214. else if (typeof path === 'string') {
  215. // @ts-expect-error in this case `contents` is always a BinaryFileContents
  216. file.contents = contents !== null && contents !== void 0 ? contents : [];
  217. }
  218. return tauri.invokeTauriCommand({
  219. __tauriModule: 'Fs',
  220. message: {
  221. cmd: 'writeFile',
  222. path: file.path,
  223. contents: Array.from(file.contents instanceof ArrayBuffer
  224. ? new Uint8Array(file.contents)
  225. : file.contents),
  226. options: fileOptions
  227. }
  228. });
  229. }
  230. /**
  231. * List directory files.
  232. * @example
  233. * ```typescript
  234. * import { readDir, BaseDirectory } from '@tauri-apps/api/fs';
  235. * // Reads the `$APPDATA/users` directory recursively
  236. * const entries = await readDir('users', { dir: BaseDirectory.AppData, recursive: true });
  237. *
  238. * function processEntries(entries) {
  239. * for (const entry of entries) {
  240. * console.log(`Entry: ${entry.path}`);
  241. * if (entry.children) {
  242. * processEntries(entry.children)
  243. * }
  244. * }
  245. * }
  246. * ```
  247. *
  248. * @since 1.0.0
  249. */
  250. async function readDir(dir, options = {}) {
  251. return tauri.invokeTauriCommand({
  252. __tauriModule: 'Fs',
  253. message: {
  254. cmd: 'readDir',
  255. path: dir,
  256. options
  257. }
  258. });
  259. }
  260. /**
  261. * Creates a directory.
  262. * If one of the path's parent components doesn't exist
  263. * and the `recursive` option isn't set to true, the promise will be rejected.
  264. * @example
  265. * ```typescript
  266. * import { createDir, BaseDirectory } from '@tauri-apps/api/fs';
  267. * // Create the `$APPDATA/users` directory
  268. * await createDir('users', { dir: BaseDirectory.AppData, recursive: true });
  269. * ```
  270. *
  271. * @returns A promise indicating the success or failure of the operation.
  272. *
  273. * @since 1.0.0
  274. */
  275. async function createDir(dir, options = {}) {
  276. return tauri.invokeTauriCommand({
  277. __tauriModule: 'Fs',
  278. message: {
  279. cmd: 'createDir',
  280. path: dir,
  281. options
  282. }
  283. });
  284. }
  285. /**
  286. * Removes a directory.
  287. * If the directory is not empty and the `recursive` option isn't set to true, the promise will be rejected.
  288. * @example
  289. * ```typescript
  290. * import { removeDir, BaseDirectory } from '@tauri-apps/api/fs';
  291. * // Remove the directory `$APPDATA/users`
  292. * await removeDir('users', { dir: BaseDirectory.AppData });
  293. * ```
  294. *
  295. * @returns A promise indicating the success or failure of the operation.
  296. *
  297. * @since 1.0.0
  298. */
  299. async function removeDir(dir, options = {}) {
  300. return tauri.invokeTauriCommand({
  301. __tauriModule: 'Fs',
  302. message: {
  303. cmd: 'removeDir',
  304. path: dir,
  305. options
  306. }
  307. });
  308. }
  309. /**
  310. * Copies a file to a destination.
  311. * @example
  312. * ```typescript
  313. * import { copyFile, BaseDirectory } from '@tauri-apps/api/fs';
  314. * // Copy the `$APPCONFIG/app.conf` file to `$APPCONFIG/app.conf.bk`
  315. * await copyFile('app.conf', 'app.conf.bk', { dir: BaseDirectory.AppConfig });
  316. * ```
  317. *
  318. * @returns A promise indicating the success or failure of the operation.
  319. *
  320. * @since 1.0.0
  321. */
  322. async function copyFile(source, destination, options = {}) {
  323. return tauri.invokeTauriCommand({
  324. __tauriModule: 'Fs',
  325. message: {
  326. cmd: 'copyFile',
  327. source,
  328. destination,
  329. options
  330. }
  331. });
  332. }
  333. /**
  334. * Removes a file.
  335. * @example
  336. * ```typescript
  337. * import { removeFile, BaseDirectory } from '@tauri-apps/api/fs';
  338. * // Remove the `$APPConfig/app.conf` file
  339. * await removeFile('app.conf', { dir: BaseDirectory.AppConfig });
  340. * ```
  341. *
  342. * @returns A promise indicating the success or failure of the operation.
  343. *
  344. * @since 1.0.0
  345. */
  346. async function removeFile(file, options = {}) {
  347. return tauri.invokeTauriCommand({
  348. __tauriModule: 'Fs',
  349. message: {
  350. cmd: 'removeFile',
  351. path: file,
  352. options
  353. }
  354. });
  355. }
  356. /**
  357. * Renames a file.
  358. * @example
  359. * ```typescript
  360. * import { renameFile, BaseDirectory } from '@tauri-apps/api/fs';
  361. * // Rename the `$APPDATA/avatar.png` file
  362. * await renameFile('avatar.png', 'deleted.png', { dir: BaseDirectory.AppData });
  363. * ```
  364. *
  365. * @returns A promise indicating the success or failure of the operation.
  366. *
  367. * @since 1.0.0
  368. */
  369. async function renameFile(oldPath, newPath, options = {}) {
  370. return tauri.invokeTauriCommand({
  371. __tauriModule: 'Fs',
  372. message: {
  373. cmd: 'renameFile',
  374. oldPath,
  375. newPath,
  376. options
  377. }
  378. });
  379. }
  380. /**
  381. * Check if a path exists.
  382. * @example
  383. * ```typescript
  384. * import { exists, BaseDirectory } from '@tauri-apps/api/fs';
  385. * // Check if the `$APPDATA/avatar.png` file exists
  386. * await exists('avatar.png', { dir: BaseDirectory.AppData });
  387. * ```
  388. *
  389. * @since 1.1.0
  390. */
  391. async function exists(path, options = {}) {
  392. return tauri.invokeTauriCommand({
  393. __tauriModule: 'Fs',
  394. message: {
  395. cmd: 'exists',
  396. path,
  397. options
  398. }
  399. });
  400. }
  401. exports.BaseDirectory = exports.Dir;
  402. exports.copyFile = copyFile;
  403. exports.createDir = createDir;
  404. exports.exists = exists;
  405. exports.readBinaryFile = readBinaryFile;
  406. exports.readDir = readDir;
  407. exports.readTextFile = readTextFile;
  408. exports.removeDir = removeDir;
  409. exports.removeFile = removeFile;
  410. exports.renameFile = renameFile;
  411. exports.writeBinaryFile = writeBinaryFile;
  412. exports.writeFile = writeTextFile;
  413. exports.writeTextFile = writeTextFile;