├── .github └── workflows │ ├── jsr.yml │ ├── test.yml │ └── udd.yml ├── .gitignore ├── .gitmessage ├── LICENSE ├── README.md ├── deno.jsonc └── mod.ts /.github/workflows/jsr.yml: -------------------------------------------------------------------------------- 1 | name: jsr 2 | 3 | env: 4 | DENO_VERSION: 1.x 5 | 6 | on: 7 | push: 8 | tags: 9 | - "v*" 10 | 11 | permissions: 12 | contents: read 13 | id-token: write 14 | 15 | jobs: 16 | publish: 17 | runs-on: ubuntu-latest 18 | steps: 19 | - uses: actions/checkout@v4 20 | with: 21 | fetch-depth: 0 22 | - uses: denoland/setup-deno@v1 23 | with: 24 | deno-version: ${{ env.DENO_VERSION }} 25 | - name: Publish 26 | run: | 27 | deno run -A jsr:@david/publish-on-tag@0.1.3 28 | -------------------------------------------------------------------------------- /.github/workflows/test.yml: -------------------------------------------------------------------------------- 1 | name: Test 2 | 3 | env: 4 | DENO_VERSION: 1.x 5 | 6 | on: 7 | schedule: 8 | - cron: "0 7 * * 0" 9 | push: 10 | branches: 11 | - main 12 | pull_request: 13 | 14 | jobs: 15 | check: 16 | runs-on: ubuntu-latest 17 | steps: 18 | - uses: actions/checkout@v3 19 | - uses: denoland/setup-deno@v1 20 | with: 21 | deno-version: ${{ env.DENO_VERSION }} 22 | - name: Format 23 | run: | 24 | deno fmt --check 25 | - name: Lint 26 | run: deno lint 27 | - name: Type check 28 | run: deno task check 29 | 30 | test: 31 | runs-on: ubuntu-latest 32 | steps: 33 | - uses: actions/checkout@v4 34 | - uses: denoland/setup-deno@v1 35 | with: 36 | deno-version: ${{ env.DENO_VERSION }} 37 | - name: Test 38 | run: | 39 | deno task test 40 | timeout-minutes: 5 41 | - name: JSR publish (dry-run) 42 | run: | 43 | deno publish --dry-run 44 | -------------------------------------------------------------------------------- /.github/workflows/udd.yml: -------------------------------------------------------------------------------- 1 | name: Update 2 | 3 | on: 4 | schedule: 5 | - cron: "0 0 * * *" 6 | workflow_dispatch: 7 | 8 | jobs: 9 | udd: 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | - uses: denoland/setup-deno@v1 14 | with: 15 | deno-version: "1.x" 16 | - name: Update dependencies 17 | run: | 18 | deno task upgrade > ../output.txt 19 | env: 20 | NO_COLOR: 1 21 | - name: Read ../output.txt 22 | id: log 23 | uses: juliangruber/read-file-action@v1 24 | with: 25 | path: ../output.txt 26 | - name: Commit changes 27 | run: | 28 | git config user.name '${{ github.actor }}' 29 | git config user.email '${{ github.actor }}@users.noreply.github.com' 30 | git commit -a -F- < 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | 21 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # systemopen 2 | 3 | [![jsr](https://img.shields.io/jsr/v/%40lambdalisue/systemopen?logo=javascript&logoColor=white)](https://jsr.io/@lambdalisue/systemopen) 4 | [![denoland](https://img.shields.io/github/v/release/lambdalisue/deno-systemopen?logo=deno&label=denoland)](https://github.com/lambdalisue/deno-systemopen/releases) 5 | [![deno doc](https://doc.deno.land/badge.svg)](https://doc.deno.land/https/deno.land/x/systemopen/mod.ts) 6 | [![Test](https://github.com/lambdalisue/deno-systemopen/workflows/Test/badge.svg)](https://github.com/lambdalisue/deno-systemopen/actions?query=workflow%3ATest) 7 | 8 | Open a file or URL using the default application of the user's OS. It supports 9 | Windows, macOS, and Linux. 10 | 11 | ## Usage 12 | 13 | ```ts 14 | import { systemopen } from "./mod.ts"; 15 | 16 | if (await systemopen("https://deno.land")) { 17 | console.log("Success"); 18 | } else { 19 | console.log("Failed"); 20 | } 21 | ``` 22 | 23 | ## License 24 | 25 | The code follows MIT license written in [LICENSE](./LICENSE). Contributors need 26 | to agree that any modifications sent in this repository follow the license. 27 | -------------------------------------------------------------------------------- /deno.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lambdalisue/systemopen", 3 | "version": "0.0.0", 4 | "exports": "./mod.ts", 5 | "lock": false, 6 | "tasks": { 7 | "test": "deno test --unstable -A --parallel --doc", 8 | "check": "deno check --unstable $(find . -name '*.ts')", 9 | "upgrade": "deno run -A https://deno.land/x/udd/main.ts $(find . -name '*.ts' -not -path '*/npm/*')" 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | type Opener = (path: string) => [string, string[]]; 2 | 3 | let validOpener: Opener | undefined; 4 | 5 | /** 6 | * Opens the given path with the system default application. 7 | */ 8 | export async function systemopen(path: string): Promise { 9 | const openers = validOpener ? [validOpener] : openerCandidates[Deno.build.os]; 10 | for (const opener of openers) { 11 | try { 12 | if (await openWith(opener, path)) { 13 | validOpener = opener; 14 | return true; 15 | } 16 | } catch (error) { 17 | if (error instanceof Deno.errors.NotFound) { 18 | continue; 19 | } 20 | throw error; 21 | } 22 | } 23 | return false; 24 | } 25 | 26 | async function openWith(opener: Opener, path: string): Promise { 27 | const [cmd, args] = opener(path); 28 | const command = new Deno.Command(cmd, { 29 | args, 30 | stdin: "null", 31 | stdout: "null", 32 | stderr: "null", 33 | }); 34 | const proc = command.spawn(); 35 | const { success } = await proc.status; 36 | return success; 37 | } 38 | 39 | const openerCandidates: Record = { 40 | linux: [ 41 | (path: string) => ["xdg-open", [path]], 42 | (path: string) => ["wslview", [path]], 43 | (path: string) => ["gnome-open", [path]], 44 | (path: string) => ["kde-open", [path]], 45 | (path: string) => ["exo-open", [path]], 46 | ], 47 | darwin: [ 48 | (path: string) => ["open", [path]], 49 | ], 50 | windows: [ 51 | (path: string) => ["rundll32", ["url.dll,FileProtocolHandler", path]], 52 | ], 53 | }; 54 | --------------------------------------------------------------------------------