From 9d58ee816ed5535461af90eda2c92bc705e22d27 Mon Sep 17 00:00:00 2001 From: baptistecdr Date: Mon, 23 Oct 2023 20:28:59 +0200 Subject: [PATCH] Implement use complete path #298 --- package-lock.json | 80 ++++++++++++++++++- package.json | 6 +- public/_locales/en/messages.json | 15 +++- public/_locales/fr/messages.json | 15 +++- public/_locales/zh_CN/messages.json | 15 +++- public/manifest.json | 2 +- src/background/background.ts | 11 +-- src/models/aria2-extension.ts | 19 +++-- src/models/basename.ts | 12 --- src/models/extension-options.ts | 3 +- .../components/extension-options-tab.tsx | 49 +++++++++++- src/options/options.tsx | 20 +++-- src/popup/components/server-quick-options.tsx | 28 +++++++ src/popup/components/server-task.tsx | 2 +- src/popup/popup.tsx | 28 ++++--- src/stdlib.ts | 23 ++++++ 16 files changed, 267 insertions(+), 61 deletions(-) delete mode 100644 src/models/basename.ts create mode 100644 src/stdlib.ts diff --git a/package-lock.json b/package-lock.json index b2d20ffe..a0886bb4 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,15 +1,17 @@ { "name": "aria2-integration", - "version": "4.5.4-SNAPSHOT.0", + "version": "4.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "aria2-integration", - "version": "4.5.4-SNAPSHOT.0", + "version": "4.6.0", "dependencies": { "@stdlib/regexp-basename-posix": "0.1.1", "@stdlib/regexp-basename-windows": "0.1.1", + "@stdlib/regexp-dirname-posix": "0.1.1", + "@stdlib/regexp-dirname-windows": "0.1.1", "@testing-library/jest-dom": "6.1.4", "@testing-library/react": "14.0.0", "@testing-library/user-event": "14.5.1", @@ -30,10 +32,12 @@ "webextension-polyfill": "0.10.0" }, "devDependencies": { + "@types/bootstrap": "5.2.8", "@types/jest": "29.5.6", "@types/luxon": "3.3.3", "@types/node": "20.8.9", "@types/react": "18.2.33", + "@types/react-bootstrap": "0.32.34", "@types/react-dom": "18.2.14", "@types/uuid": "9.0.6", "@types/webextension-polyfill": "0.10.5", @@ -964,6 +968,60 @@ "url": "https://opencollective.com/stdlib" } }, + "node_modules/@stdlib/regexp-dirname-posix": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@stdlib/regexp-dirname-posix/-/regexp-dirname-posix-0.1.1.tgz", + "integrity": "sha512-tj0BQSwFQ6Amm5Y/J7x+Vz/p9gVppArJ8czx/hdOcE1w6heaFohiZ8SRhJeIQLdVDub/eWWi9jBlFcAN3LZMXA==", + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "dependencies": { + "@stdlib/utils-define-nonenumerable-read-only-property": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stdlib" + } + }, + "node_modules/@stdlib/regexp-dirname-windows": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@stdlib/regexp-dirname-windows/-/regexp-dirname-windows-0.1.1.tgz", + "integrity": "sha512-g3V+S+i2CqZ9kahj7mlZCrM2EjNu8sPiWdBRT6xDupANuwvqZnKxXkPz71efTpJYcWsbnzGOlxiMyKK6W+xyMA==", + "os": [ + "aix", + "darwin", + "freebsd", + "linux", + "macos", + "openbsd", + "sunos", + "win32", + "windows" + ], + "dependencies": { + "@stdlib/utils-define-nonenumerable-read-only-property": "^0.1.1" + }, + "engines": { + "node": ">=0.10.0", + "npm": ">2.7.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/stdlib" + } + }, "node_modules/@stdlib/string-base-format-interpolate": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/@stdlib/string-base-format-interpolate/-/string-base-format-interpolate-0.1.1.tgz", @@ -1270,6 +1328,15 @@ "@babel/types": "^7.20.7" } }, + "node_modules/@types/bootstrap": { + "version": "5.2.8", + "resolved": "https://registry.npmjs.org/@types/bootstrap/-/bootstrap-5.2.8.tgz", + "integrity": "sha512-14do+aWZPc1w3G+YevSsy8eas1XEPhTOUNBhQX/r12YKn7ySssATJusBQ/HCQAd2nq54U8vvrftHSb1YpeJUXg==", + "dev": true, + "dependencies": { + "@popperjs/core": "^2.9.2" + } + }, "node_modules/@types/eslint": { "version": "8.40.0", "license": "MIT", @@ -1400,6 +1467,15 @@ "csstype": "^3.0.2" } }, + "node_modules/@types/react-bootstrap": { + "version": "0.32.34", + "resolved": "https://registry.npmjs.org/@types/react-bootstrap/-/react-bootstrap-0.32.34.tgz", + "integrity": "sha512-Tx1EOJysc3hDmoVFsHz6IIMbF26nMaaki+CF6c7XA1TJ2g0/JYAXn2fObuNB559vflobRL/w6Ht1bBCiirpOjQ==", + "dev": true, + "dependencies": { + "@types/react": "*" + } + }, "node_modules/@types/react-dom": { "version": "18.2.14", "resolved": "https://registry.npmjs.org/@types/react-dom/-/react-dom-18.2.14.tgz", diff --git a/package.json b/package.json index 05544cf2..2e357dea 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "name": "aria2-integration", "private": true, "type": "module", - "version": "4.5.4-SNAPSHOT.0", + "version": "4.6.0", "scripts": { "lint": "eslint 'src/**/*.{js,ts,jsx,tsx}'", "lint:fix": "eslint 'src/**/*.{js,ts,jsx,tsx}' --fix", @@ -14,6 +14,8 @@ "dependencies": { "@stdlib/regexp-basename-posix": "0.1.1", "@stdlib/regexp-basename-windows": "0.1.1", + "@stdlib/regexp-dirname-posix": "0.1.1", + "@stdlib/regexp-dirname-windows": "0.1.1", "@testing-library/jest-dom": "6.1.4", "@testing-library/react": "14.0.0", "@testing-library/user-event": "14.5.1", @@ -40,6 +42,8 @@ "@types/react": "18.2.33", "@types/react-dom": "18.2.14", "@types/uuid": "9.0.6", + "@types/bootstrap": "5.2.8", + "@types/react-bootstrap": "0.32.34", "@types/webextension-polyfill": "0.10.5", "@typescript-eslint/eslint-plugin": "6.9.0", "@typescript-eslint/parser": "6.9.0", diff --git a/public/_locales/en/messages.json b/public/_locales/en/messages.json index 23b31f1a..e9a7d7f8 100644 --- a/public/_locales/en/messages.json +++ b/public/_locales/en/messages.json @@ -5,9 +5,6 @@ "extDescription": { "message": "Download files with Aria2" }, - "optionsNoServers": { - "message": "There are no servers
Create a new server using the + button above." - }, "serverOptionsSuccess": { "message": "The options have been saved." }, @@ -83,6 +80,18 @@ "extensionOptionsExcludeFileTypesDescription": { "message": "rar, zip" }, + "extensionOptionsUseCompleteFilePath": { + "message": "Use complete file path when using \"Save Link As...\"" + }, + "extensionOptionsUseCompleteFilePathHelpTitle": { + "message": "Help" + }, + "extensionOptionsUseCompleteFilePathHelpContent": { + "message": "Aria2 Integration uses the file name when you are using \"Save Link As...\".\nEnabling this option, Aria2 Integration will also use the file path. It only works when Aria2 is running locally." + }, + "extensionOptionsUseCompleteFilePathHelpButton": { + "message": "Close" + }, "extensionOptionsExcludeFileTypesInformation": { "message": "Leave it blank to capture files of any type." }, diff --git a/public/_locales/fr/messages.json b/public/_locales/fr/messages.json index 04417a9e..b79e23f3 100644 --- a/public/_locales/fr/messages.json +++ b/public/_locales/fr/messages.json @@ -5,9 +5,6 @@ "extDescription": { "message": "Télécharger des fichiers avec Aria2" }, - "optionsNoServers": { - "message": "Il n'y a aucun serveur.
Créez-en un en cliquant sur le bouton + au dessus." - }, "serverOptionsSuccess": { "message": "Les options ont été sauvegardé." }, @@ -86,6 +83,18 @@ "extensionOptionsExcludeFileTypesInformation": { "message": "rar, zip" }, + "extensionOptionsUseCompleteFilePath": { + "message": "Utiliser le chemin d'accès complet au fichier lors de l'utilisation \"Enregistrer le lien sous...\"" + }, + "extensionOptionsUseCompleteFilePathHelpTitle": { + "message": "Aide" + }, + "extensionOptionsUseCompleteFilePathHelpContent": { + "message": "Aria2 Integration utilise le nom du fichier lorsque vous utilisez \"Enregistrer le lien sous...\".\nEn activant cette option, Aria2 Integration utilisera également le chemin d'accès au fichier. Cela ne fonctionne que lorsque Aria2 est exécuté localement." + }, + "extensionOptionsUseCompleteFilePathHelpButton": { + "message": "Fermer" + }, "extensionOptionsTheme": { "message": "Thème" }, diff --git a/public/_locales/zh_CN/messages.json b/public/_locales/zh_CN/messages.json index 2192ae2c..2f41bb7b 100644 --- a/public/_locales/zh_CN/messages.json +++ b/public/_locales/zh_CN/messages.json @@ -5,9 +5,6 @@ "extDescription": { "message": "用 Aria2 下载文件" }, - "optionsNoServers": { - "message": "无服务器
使用上面的 + 按钮创建新的服务器." - }, "serverOptionsSuccess": { "message": "选项已保存." }, @@ -83,6 +80,18 @@ "extensionOptionsExcludeFileTypesDescription": { "message": "rar, zip" }, + "extensionOptionsUseCompleteFilePath": { + "message": "Use complete file path when using \"Save Link As...\"" + }, + "extensionOptionsUseCompleteFilePathHelpTitle": { + "message": "Help" + }, + "extensionOptionsUseCompleteFilePathHelpContent": { + "message": "Aria2 Integration uses the file name when you are using \"Save Link As...\".\nEnabling this option, Aria2 Integration will also use the file path. It only works when Aria2 is running locally." + }, + "extensionOptionsUseCompleteFilePathHelpButton": { + "message": "Close" + }, "extensionOptionsExcludeFileTypesInformation": { "message": "留空则拦截任何类型的文件下载." }, diff --git a/public/manifest.json b/public/manifest.json index 7540b0af..eefb69ca 100644 --- a/public/manifest.json +++ b/public/manifest.json @@ -4,7 +4,7 @@ "author": "baptistecdr", "homepage_url": "https://github.com/baptistecdr/aria2-extensions", "description": "__MSG_extDescription__", - "version": "4.5.4-SNAPSHOT.0", + "version": "4.6.0", "default_locale": "en", "permissions": [ "contextMenus", diff --git a/src/background/background.ts b/src/background/background.ts index 91ee0091..d613f01c 100644 --- a/src/background/background.ts +++ b/src/background/background.ts @@ -5,7 +5,7 @@ import type { Cookies, Downloads, Menus, Tabs } from "webextension-polyfill"; import browser from "webextension-polyfill"; import { captureTorrentFromURL, captureURL, showNotification } from "../models/aria2-extension"; import ExtensionOptions from "../models/extension-options"; -import basename from "../models/basename"; +import { basename, dirname } from "../stdlib"; import Server from "../models/server"; const CONTEXT_MENUS_PARENT_ID = "aria2-integration"; @@ -144,15 +144,16 @@ function downloadItemMustBeCaptured(extensionOptions: ExtensionOptions, item: Do return false; } -async function captureDownloadItem(aria2: any, server: Server, item: Downloads.DownloadItem, referer: string, cookies: string) { +async function captureDownloadItem(aria2: any, server: Server, item: Downloads.DownloadItem, referer: string, cookies: string, useCompleteFilePath: boolean) { // eslint-disable-next-line @typescript-eslint/ban-ts-comment // @ts-ignore const url = item.finalUrl ?? item.url; // finalUrl (Chrome), url (Firefox) + const directory = useCompleteFilePath ? await dirname(item.filename) : undefined; const filename = await basename(item.filename); if (url.match(/\.torrent$|\.meta4$|\.metalink$/) || filename.match(/\.torrent$|\.meta4$|\.metalink$/)) { - return captureTorrentFromURL(aria2, server, url, filename); + return captureTorrentFromURL(aria2, server, url, directory, filename); } - return captureURL(aria2, server, url, referer, cookies, filename); + return captureURL(aria2, server, url, referer, cookies, directory, filename); } browser.downloads.onCreated.addListener(async (downloadItem) => { @@ -175,7 +176,7 @@ browser.downloads.onCreated.addListener(async (downloadItem) => { await browser.downloads.erase({ id: downloadItem.id }); } try { - await captureDownloadItem(connection, server, downloadItem, referrer, cookies); + await captureDownloadItem(connection, server, downloadItem, referrer, cookies, extensionOptions.useCompleteFilePath); await showNotification(browser.i18n.getMessage("addFileSuccess", server.name)); } catch { await showNotification(browser.i18n.getMessage("addFileError", server.name)); diff --git a/src/models/aria2-extension.ts b/src/models/aria2-extension.ts index 2655897b..f6df9a7d 100644 --- a/src/models/aria2-extension.ts +++ b/src/models/aria2-extension.ts @@ -47,23 +47,32 @@ export async function captureTorrentFromFile(aria2: any, server: Server, file: F return aria2.call("aria2.addMetalink", blobAsBase64, [], server.rpcParameters); } -export async function captureTorrentFromURL(aria2: any, server: Server, url: string, filename?: string) { +export async function captureTorrentFromURL(aria2: any, server: Server, url: string, directory?: string, filename?: string) { const blob = await download(url); const blobAsBase64 = await encodeFileToBase64(blob); + const aria2Parameters: any = { + ...server.rpcParameters, + }; + if (directory) { + aria2Parameters.dir = directory; + } if (url.endsWith(".torrent") || filename?.endsWith(".torrent")) { - return aria2.call("aria2.addTorrent", blobAsBase64, [], server.rpcParameters); + return aria2.call("aria2.addTorrent", blobAsBase64, [], aria2Parameters); } - return aria2.call("aria2.addMetalink", blobAsBase64, [], server.rpcParameters); + return aria2.call("aria2.addMetalink", blobAsBase64, [], aria2Parameters); } -export async function captureURL(aria2: any, server: Server, url: string, referer: string, cookies: string, filename?: string) { +export async function captureURL(aria2: any, server: Server, url: string, referer: string, cookies: string, directory?: string, filename?: string) { if (url.match(/\.torrent$|\.meta4$|\.metalink$/)) { - return captureTorrentFromURL(aria2, server, url, filename); + return captureTorrentFromURL(aria2, server, url, directory, filename); } const aria2Parameters: any = { header: [`Referer: ${referer}`, `Cookie: ${cookies}`], ...server.rpcParameters, }; + if (directory) { + aria2Parameters.dir = directory; + } if (filename) { aria2Parameters.out = filename; } diff --git a/src/models/basename.ts b/src/models/basename.ts deleted file mode 100644 index 753a4800..00000000 --- a/src/models/basename.ts +++ /dev/null @@ -1,12 +0,0 @@ -import reBasenameWindows from "@stdlib/regexp-basename-windows"; -import reBasenamePosix from "@stdlib/regexp-basename-posix"; -import browser from "webextension-polyfill"; - -export default async function basename(filename: string): Promise { - const isWin = (await browser.runtime.getPlatformInfo()).os === "win"; - const result = isWin ? reBasenameWindows().exec(filename) : reBasenamePosix().exec(filename); - if (result === null || result.length !== 2) { - return filename; - } - return result[1]; -} diff --git a/src/models/extension-options.ts b/src/models/extension-options.ts index 1977c4bf..18144a12 100644 --- a/src/models/extension-options.ts +++ b/src/models/extension-options.ts @@ -10,10 +10,11 @@ export default class ExtensionOptions { public readonly excludedProtocols: string[] = [], public readonly excludedSites: string[] = [], public readonly excludedFileTypes: string[] = [], + public readonly useCompleteFilePath: boolean = false, public readonly theme: Theme = Theme.Auto, ) {} - private serialize(): string { + public serialize(): string { return JSON.stringify(this); } diff --git a/src/options/components/extension-options-tab.tsx b/src/options/components/extension-options-tab.tsx index a6465f9c..2765d008 100644 --- a/src/options/components/extension-options-tab.tsx +++ b/src/options/components/extension-options-tab.tsx @@ -1,7 +1,7 @@ import { ChangeEvent, useState } from "react"; import "bootstrap/dist/css/bootstrap.css"; import "bootstrap/dist/js/bootstrap"; -import { Alert, Button, Col, Form, FormText } from "react-bootstrap"; +import { Alert, Button, Col, Form, FormText, Modal } from "react-bootstrap"; import i18n from "../../i18n"; import AlertProps from "../models/alert-props"; import ExtensionOptions from "../../models/extension-options"; @@ -18,9 +18,15 @@ function ExtensionOptionsTab({ extensionOptions, setExtensionOptions }: Props) { const [excludedProtocols, setExcludedProtocols] = useState(extensionOptions.excludedProtocols); const [excludedSites, setExcludedSites] = useState(extensionOptions.excludedSites); const [excludedFileTypes, setExcludedFileTypes] = useState(extensionOptions.excludedFileTypes); + const [useCompleteFilePath, setUseCompleteFilePath] = useState(extensionOptions.useCompleteFilePath); const [theme, setTheme] = useState(extensionOptions.theme); const [alertProps, setAlertProps] = useState(new AlertProps()); + const [show, setShow] = useState(false); + + const handleClose = () => setShow(false); + const handleShow = () => setShow(true); + function serializeExcludedOption(excludedOptions: string) { return excludedOptions .trim() @@ -56,6 +62,7 @@ function ExtensionOptionsTab({ extensionOptions, setExtensionOptions }: Props) { excludedProtocols, excludedSites, excludedFileTypes, + useCompleteFilePath, theme, ).toStorage(); setExtensionOptions(newExtensionOptions); @@ -77,8 +84,12 @@ function ExtensionOptionsTab({ extensionOptions, setExtensionOptions }: Props) { - {i18n("extensionOptionsCaptureDownloads")} - + @@ -149,6 +160,38 @@ function ExtensionOptionsTab({ extensionOptions, setExtensionOptions }: Props) { + + + setUseCompleteFilePath(e.target.checked)} + /> + + + + + + + + + + + + {i18n("extensionOptionsUseCompleteFilePathHelpTitle")} + + {i18n("extensionOptionsUseCompleteFilePathHelpContent")} + + + + + {i18n("extensionOptionsTheme")} diff --git a/src/options/options.tsx b/src/options/options.tsx index 90bd9b3d..206b6165 100644 --- a/src/options/options.tsx +++ b/src/options/options.tsx @@ -1,7 +1,7 @@ import { createRoot } from "react-dom/client"; -import "bootstrap/dist/css/bootstrap.min.css"; -import "bootstrap/dist/js/bootstrap.min"; -import { useEffect, useState } from "react"; +import "bootstrap/dist/css/bootstrap.css"; +import "bootstrap/dist/js/bootstrap"; +import React, { useEffect, useState } from "react"; import { Container, Tab, Tabs } from "react-bootstrap"; import ExtensionOptionsTab from "./components/extension-options-tab"; import ServerOptionsTab from "./components/server-options-tab"; @@ -30,10 +30,12 @@ function Options() { useEffect(() => { if (extensionOptions.theme === Theme.Auto && window.matchMedia("(prefers-color-scheme: dark)").matches) { document.documentElement.setAttribute("data-bs-theme", Theme.Dark); + } else if (extensionOptions.theme === Theme.Auto && window.matchMedia("(prefers-color-scheme: light)").matches) { + document.documentElement.setAttribute("data-bs-theme", Theme.Light); } else { document.documentElement.setAttribute("data-bs-theme", extensionOptions.theme); } - }, [extensionOptions]); + }, [extensionOptions.theme]); async function addServer() { const server = new Server(); @@ -88,14 +90,16 @@ function Options() { ))} - + ); } root.render( - - - , + + + + + , ); diff --git a/src/popup/components/server-quick-options.tsx b/src/popup/components/server-quick-options.tsx index e9251f58..4b61d5f2 100644 --- a/src/popup/components/server-quick-options.tsx +++ b/src/popup/components/server-quick-options.tsx @@ -19,6 +19,23 @@ function ServerQuickOptions({ setExtensionOptions, extensionOptions, server }: P extensionOptions.excludedProtocols, extensionOptions.excludedSites, extensionOptions.excludedFileTypes, + extensionOptions.useCompleteFilePath, + extensionOptions.theme, + ); + await newExtensionsOptions.toStorage(); + setExtensionOptions(newExtensionsOptions); + }; + + const onChangeUseCompleteFilePath = async (e: ChangeEvent) => { + const newExtensionsOptions = new ExtensionOptions( + extensionOptions.servers, + extensionOptions.captureServer, + extensionOptions.captureDownloads, + extensionOptions.excludedProtocols, + extensionOptions.excludedSites, + extensionOptions.excludedFileTypes, + e.target.checked, + extensionOptions.theme, ); await newExtensionsOptions.toStorage(); setExtensionOptions(newExtensionsOptions); @@ -36,6 +53,17 @@ function ServerQuickOptions({ setExtensionOptions, extensionOptions, server }: P /> + + + + + ); } diff --git a/src/popup/components/server-task.tsx b/src/popup/components/server-task.tsx index 3902f7f9..99ce13be 100644 --- a/src/popup/components/server-task.tsx +++ b/src/popup/components/server-task.tsx @@ -4,7 +4,7 @@ import browser from "webextension-polyfill"; import { filesize, FileSizeOptionsBase } from "filesize"; import { useEffect, useState } from "react"; import { Task } from "../models/task"; -import basename from "../../models/basename"; +import { basename } from "../../stdlib"; import ServerTaskManagement from "./server-task-management"; import i18n from "../../i18n"; diff --git a/src/popup/popup.tsx b/src/popup/popup.tsx index 0d443982..b0bd99cf 100644 --- a/src/popup/popup.tsx +++ b/src/popup/popup.tsx @@ -1,8 +1,8 @@ import { createRoot } from "react-dom/client"; -import { useEffect, useState } from "react"; +import React, { useEffect, useState } from "react"; import "bootstrap-icons/font/bootstrap-icons.css"; -import "bootstrap/dist/css/bootstrap.min.css"; -import "bootstrap/dist/js/bootstrap.min"; +import "bootstrap/dist/css/bootstrap.css"; +import "bootstrap/dist/js/bootstrap"; import "./popup.css"; import { Container, Tab, Tabs } from "react-bootstrap"; import i18n from "../i18n.js"; @@ -24,13 +24,13 @@ function Servers() { }); }, []); - useEffect(() => { - if (extensionOptions.theme === Theme.Auto && window.matchMedia("(prefers-color-scheme: dark)").matches) { - document.documentElement.setAttribute("data-bs-theme", Theme.Dark); - } else { - document.documentElement.setAttribute("data-bs-theme", extensionOptions.theme); - } - }, [extensionOptions]); + if (extensionOptions.theme === Theme.Auto && window.matchMedia("(prefers-color-scheme: dark)").matches) { + document.documentElement.setAttribute("data-bs-theme", Theme.Dark); + } else if (extensionOptions.theme === Theme.Auto && window.matchMedia("(prefers-color-scheme: light)").matches) { + document.documentElement.setAttribute("data-bs-theme", Theme.Light); + } else { + document.documentElement.setAttribute("data-bs-theme", extensionOptions.theme); + } if (Object.keys(extensionOptions.servers).length === 0) { return ( @@ -63,7 +63,9 @@ function Servers() { } root.render( - - - , + + + + + , ); diff --git a/src/stdlib.ts b/src/stdlib.ts new file mode 100644 index 00000000..b6ada200 --- /dev/null +++ b/src/stdlib.ts @@ -0,0 +1,23 @@ +import reBasenameWindows from "@stdlib/regexp-basename-windows"; +import reBasenamePosix from "@stdlib/regexp-basename-posix"; +import browser from "webextension-polyfill"; +import reDirnameWindows from "@stdlib/regexp-dirname-windows"; +import reDirnamePosix from "@stdlib/regexp-dirname-posix"; + +export async function basename(filename: string): Promise { + const isWin = (await browser.runtime.getPlatformInfo()).os === "win"; + const result = isWin ? reBasenameWindows().exec(filename) : reBasenamePosix().exec(filename); + if (result === null || result.length !== 2) { + return filename; + } + return result[1]; +} + +export async function dirname(filename: string): Promise { + const isWin = (await browser.runtime.getPlatformInfo()).os === "win"; + const result = isWin ? reDirnameWindows().exec(filename) : reDirnamePosix().exec(filename); + if (result === null || result.length !== 2) { + return filename; + } + return result[1]; +}