├── .github └── workflows │ ├── jsr.yml │ └── test.yml ├── .gitignore ├── LICENSE ├── README.md ├── deno.jsonc ├── deno.lock ├── mod.ts ├── sandbox.ts └── sandbox_test.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 | push: 8 | branches: 9 | - main 10 | pull_request: 11 | workflow_dispatch: 12 | 13 | jobs: 14 | check: 15 | runs-on: ubuntu-latest 16 | steps: 17 | - uses: actions/checkout@v4 18 | - uses: denoland/setup-deno@v1 19 | with: 20 | deno-version: ${{ env.DENO_VERSION }} 21 | - name: Format 22 | run: | 23 | deno fmt --check 24 | - name: Lint 25 | run: deno lint 26 | - name: Type check 27 | run: deno task check 28 | 29 | test: 30 | runs-on: ubuntu-latest 31 | steps: 32 | - uses: actions/checkout@v4 33 | - uses: denoland/setup-deno@v1 34 | with: 35 | deno-version: ${{ env.DENO_VERSION }} 36 | - name: Test 37 | run: | 38 | deno task test:coverage 39 | timeout-minutes: 5 40 | - run: | 41 | deno task coverage --lcov > coverage.lcov 42 | - uses: codecov/codecov-action@v4 43 | with: 44 | os: ${{ runner.os }} 45 | files: ./coverage.lcov 46 | token: ${{ secrets.CODECOV_TOKEN }} 47 | 48 | jsr-publish: 49 | runs-on: ubuntu-latest 50 | steps: 51 | - uses: actions/checkout@v4 52 | - uses: denoland/setup-deno@v1 53 | with: 54 | deno-version: ${{ env.DENO_VERSION }} 55 | - name: Publish (dry-run) 56 | run: | 57 | deno publish --dry-run 58 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .coverage 2 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright 2021 Alisue 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 | # sandbox 2 | 3 | [![JSR](https://jsr.io/badges/@lambdalisue/sandbox)](https://jsr.io/@lambdalisue/sandbox) 4 | [![Test](https://github.com/lambdalisue/deno-sandbox/actions/workflows/test.yml/badge.svg)](https://github.com/lambdalisue/deno-sandbox/actions/workflows/test.yml) 5 | [![codecov](https://codecov.io/gh/lambdalisue/deno-sandbox/graph/badge.svg?token=AEZJlup3Et)](https://codecov.io/gh/lambdalisue/deno-sandbox) 6 | 7 | This module provides `sandbox()` and `sandboxSync()` function to create a 8 | temporary sandbox directory and temporary move into it. 9 | 10 | ## Usage 11 | 12 | ```ts 13 | import { sandbox, sandboxSync } from "@lambdalisue/sandbox"; 14 | 15 | { 16 | await using sbox = await sandbox(); 17 | // The current working directory is changed to the sandbox directory here. 18 | // Do what ever you want 19 | } 20 | // The current working directory is changed back to the original directory here. 21 | 22 | { 23 | using sbox = sandboxSync(); 24 | // The current working directory is changed to the sandbox directory here. 25 | // Do what ever you want 26 | } 27 | // The current working directory is changed back to the original directory here. 28 | ``` 29 | 30 | ## License 31 | 32 | The code follows MIT license written in [LICENSE](./LICENSE). Contributors need 33 | to agree that any modifications sent in this repository follow the license. 34 | -------------------------------------------------------------------------------- /deno.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@lambdalisue/sandbox", 3 | "version": "0.0.0", 4 | "exports": { 5 | ".": "./mod.ts" 6 | }, 7 | "publish": { 8 | "include": [ 9 | "**/*.ts", 10 | "README.md", 11 | "LICENSE" 12 | ], 13 | "exclude": [ 14 | "**/*_test.ts", 15 | "**/*_bench.ts", 16 | ".*" 17 | ] 18 | }, 19 | "exclude": [ 20 | ".coverage/**" 21 | ], 22 | "tasks": { 23 | "check": "deno check ./**/*.ts", 24 | "test": "deno test -A --parallel --shuffle --doc", 25 | "test:coverage": "deno task test --coverage=.coverage", 26 | "coverage": "deno coverage .coverage --exclude=testdata/", 27 | "update": "deno run --allow-env --allow-read --allow-write=. --allow-run=git,deno --allow-net=deno.land,jsr.io,registry.npmjs.org jsr:@molt/cli ./**/*.ts", 28 | "update:write": "deno task -q update --write", 29 | "update:commit": "deno task -q update --commit --prefix :package: --pre-commit=fmt,lint" 30 | }, 31 | "imports": { 32 | "@lambdalisue/sandbox": "./mod.ts", 33 | "@std/assert": "jsr:@std/assert@^1.0.5", 34 | "@std/fs": "jsr:@std/fs@^1.0.3" 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /deno.lock: -------------------------------------------------------------------------------- 1 | { 2 | "version": "3", 3 | "packages": { 4 | "specifiers": { 5 | "jsr:@nick/dispose@^1.1.0": "jsr:@nick/dispose@1.1.0", 6 | "jsr:@std/assert@^1.0.5": "jsr:@std/assert@1.0.5", 7 | "jsr:@std/fs@^1.0.3": "jsr:@std/fs@1.0.3", 8 | "jsr:@std/internal@^1.0.3": "jsr:@std/internal@1.0.3", 9 | "jsr:@std/path@^1.0.4": "jsr:@std/path@1.0.6", 10 | "jsr:@std/path@^1.0.6": "jsr:@std/path@1.0.6", 11 | "npm:@types/node": "npm:@types/node@18.16.19" 12 | }, 13 | "jsr": { 14 | "@nick/dispose@1.1.0": { 15 | "integrity": "73a15b90cb48e1435801258ba3ac7a19382823349dedcacbb8e5b5b673ce9d17" 16 | }, 17 | "@std/assert@1.0.5": { 18 | "integrity": "e37da8e4033490ce613eec4ac1d78dba1faf5b02a3f6c573a28f15365b9b440f", 19 | "dependencies": [ 20 | "jsr:@std/internal@^1.0.3" 21 | ] 22 | }, 23 | "@std/fs@1.0.3": { 24 | "integrity": "3cb839b1360b0a42d8b367c3093bfe4071798e6694fa44cf1963e04a8edba4fe", 25 | "dependencies": [ 26 | "jsr:@std/path@^1.0.4" 27 | ] 28 | }, 29 | "@std/internal@1.0.3": { 30 | "integrity": "208e9b94a3d5649bd880e9ca38b885ab7651ab5b5303a56ed25de4755fb7b11e" 31 | }, 32 | "@std/path@1.0.6": { 33 | "integrity": "ab2c55f902b380cf28e0eec501b4906e4c1960d13f00e11cfbcd21de15f18fed" 34 | } 35 | }, 36 | "npm": { 37 | "@types/node@18.16.19": { 38 | "integrity": "sha512-IXl7o+R9iti9eBW4Wg2hx1xQDig183jj7YLn8F7udNceyfkbn1ZxmzZXuak20gR40D7pIkIY1kYGx5VIGbaHKA==", 39 | "dependencies": {} 40 | } 41 | } 42 | }, 43 | "redirects": { 44 | "https://deno.land/x/disposable/mod.ts": "https://deno.land/x/disposable@v1.2.0/mod.ts", 45 | "https://deno.land/x/sandbox/mod.ts": "https://deno.land/x/sandbox@v1.2.0/mod.ts" 46 | }, 47 | "remote": { 48 | "https://deno.land/std@0.203.0/assert/_constants.ts": "8a9da298c26750b28b326b297316cdde860bc237533b07e1337c021379e6b2a9", 49 | "https://deno.land/std@0.203.0/assert/_diff.ts": "1a3c044aedf77647d6cac86b798c6417603361b66b54c53331b312caeb447aea", 50 | "https://deno.land/std@0.203.0/assert/_format.ts": "a69126e8a469009adf4cf2a50af889aca364c349797e63174884a52ff75cf4c7", 51 | "https://deno.land/std@0.203.0/assert/assert.ts": "9a97dad6d98c238938e7540736b826440ad8c1c1e54430ca4c4e623e585607ee", 52 | "https://deno.land/std@0.203.0/assert/assert_almost_equals.ts": "e15ca1f34d0d5e0afae63b3f5d975cbd18335a132e42b0c747d282f62ad2cd6c", 53 | "https://deno.land/std@0.203.0/assert/assert_array_includes.ts": "6856d7f2c3544bc6e62fb4646dfefa3d1df5ff14744d1bca19f0cbaf3b0d66c9", 54 | "https://deno.land/std@0.203.0/assert/assert_equals.ts": "d8ec8a22447fbaf2fc9d7c3ed2e66790fdb74beae3e482855d75782218d68227", 55 | "https://deno.land/std@0.203.0/assert/assert_exists.ts": "407cb6b9fb23a835cd8d5ad804e2e2edbbbf3870e322d53f79e1c7a512e2efd7", 56 | "https://deno.land/std@0.203.0/assert/assert_false.ts": "0ccbcaae910f52c857192ff16ea08bda40fdc79de80846c206bfc061e8c851c6", 57 | "https://deno.land/std@0.203.0/assert/assert_greater.ts": "ae2158a2d19313bf675bf7251d31c6dc52973edb12ac64ac8fc7064152af3e63", 58 | "https://deno.land/std@0.203.0/assert/assert_greater_or_equal.ts": "1439da5ebbe20855446cac50097ac78b9742abe8e9a43e7de1ce1426d556e89c", 59 | "https://deno.land/std@0.203.0/assert/assert_instance_of.ts": "3aedb3d8186e120812d2b3a5dea66a6e42bf8c57a8bd927645770bd21eea554c", 60 | "https://deno.land/std@0.203.0/assert/assert_is_error.ts": "c21113094a51a296ffaf036767d616a78a2ae5f9f7bbd464cd0197476498b94b", 61 | "https://deno.land/std@0.203.0/assert/assert_less.ts": "aec695db57db42ec3e2b62e97e1e93db0063f5a6ec133326cc290ff4b71b47e4", 62 | "https://deno.land/std@0.203.0/assert/assert_less_or_equal.ts": "5fa8b6a3ffa20fd0a05032fe7257bf985d207b85685fdbcd23651b70f928c848", 63 | "https://deno.land/std@0.203.0/assert/assert_match.ts": "c4083f80600bc190309903c95e397a7c9257ff8b5ae5c7ef91e834704e672e9b", 64 | "https://deno.land/std@0.203.0/assert/assert_not_equals.ts": "9f1acab95bd1f5fc9a1b17b8027d894509a745d91bac1718fdab51dc76831754", 65 | "https://deno.land/std@0.203.0/assert/assert_not_instance_of.ts": "0c14d3dfd9ab7a5276ed8ed0b18c703d79a3d106102077ec437bfe7ed912bd22", 66 | "https://deno.land/std@0.203.0/assert/assert_not_match.ts": "3796a5b0c57a1ce6c1c57883dd4286be13a26f715ea662318ab43a8491a13ab0", 67 | "https://deno.land/std@0.203.0/assert/assert_not_strict_equals.ts": "ca6c6d645e95fbc873d25320efeb8c4c6089a9a5e09f92d7c1c4b6e935c2a6ad", 68 | "https://deno.land/std@0.203.0/assert/assert_object_match.ts": "d8fc2867cfd92eeacf9cea621e10336b666de1874a6767b5ec48988838370b54", 69 | "https://deno.land/std@0.203.0/assert/assert_rejects.ts": "45c59724de2701e3b1f67c391d6c71c392363635aad3f68a1b3408f9efca0057", 70 | "https://deno.land/std@0.203.0/assert/assert_strict_equals.ts": "b1f538a7ea5f8348aeca261d4f9ca603127c665e0f2bbfeb91fa272787c87265", 71 | "https://deno.land/std@0.203.0/assert/assert_string_includes.ts": "b821d39ebf5cb0200a348863c86d8c4c4b398e02012ce74ad15666fc4b631b0c", 72 | "https://deno.land/std@0.203.0/assert/assert_throws.ts": "63784e951475cb7bdfd59878cd25a0931e18f6dc32a6077c454b2cd94f4f4bcd", 73 | "https://deno.land/std@0.203.0/assert/assertion_error.ts": "4d0bde9b374dfbcbe8ac23f54f567b77024fb67dbb1906a852d67fe050d42f56", 74 | "https://deno.land/std@0.203.0/assert/equal.ts": "9f1a46d5993966d2596c44e5858eec821859b45f783a5ee2f7a695dfc12d8ece", 75 | "https://deno.land/std@0.203.0/assert/fail.ts": "c36353d7ae6e1f7933d45f8ea51e358c8c4b67d7e7502028598fe1fea062e278", 76 | "https://deno.land/std@0.203.0/assert/mod.ts": "37c49a26aae2b254bbe25723434dc28cd7532e444cf0b481a97c045d110ec085", 77 | "https://deno.land/std@0.203.0/assert/unimplemented.ts": "d56fbeecb1f108331a380f72e3e010a1f161baa6956fd0f7cf3e095ae1a4c75a", 78 | "https://deno.land/std@0.203.0/assert/unreachable.ts": "4600dc0baf7d9c15a7f7d234f00c23bca8f3eba8b140286aaca7aa998cf9a536", 79 | "https://deno.land/std@0.203.0/fmt/colors.ts": "c51c4642678eb690dcf5ffee5918b675bf01a33fba82acf303701ae1a4f8c8d9", 80 | "https://deno.land/std@0.203.0/fs/_util.ts": "fbf57dcdc9f7bc8128d60301eece608246971a7836a3bb1e78da75314f08b978", 81 | "https://deno.land/std@0.203.0/fs/copy.ts": "23cc1c465babe5ca4d69778821e2f8addc44593e30a5ca0b902b3784eed75bb6", 82 | "https://deno.land/std@0.203.0/fs/empty_dir.ts": "2e52cd4674d18e2e007175c80449fc3d263786a1361e858d9dfa9360a6581b47", 83 | "https://deno.land/std@0.203.0/fs/ensure_dir.ts": "dc64c4c75c64721d4e3fb681f1382f803ff3d2868f08563ff923fdd20d071c40", 84 | "https://deno.land/std@0.203.0/fs/ensure_file.ts": "39ac83cc283a20ec2735e956adf5de3e8a3334e0b6820547b5772f71c49ae083", 85 | "https://deno.land/std@0.203.0/fs/ensure_link.ts": "c15e69c48556d78aae31b83e0c0ece04b7b8bc0951412f5b759aceb6fde7f0ac", 86 | "https://deno.land/std@0.203.0/fs/ensure_symlink.ts": "b389c8568f0656d145ac7ece472afe710815cccbb2ebfd19da7978379ae143fe", 87 | "https://deno.land/std@0.203.0/fs/eol.ts": "f1f2eb348a750c34500741987b21d65607f352cf7205f48f4319d417fff42842", 88 | "https://deno.land/std@0.203.0/fs/exists.ts": "cb59a853d84871d87acab0e7936a4dac11282957f8e195102c5a7acb42546bb8", 89 | "https://deno.land/std@0.203.0/fs/expand_glob.ts": "52b8b6f5b1fa585c348250da1c80ce5d820746cb4a75d874b3599646f677d3a7", 90 | "https://deno.land/std@0.203.0/fs/mod.ts": "bc3d0acd488cc7b42627044caf47d72019846d459279544e1934418955ba4898", 91 | "https://deno.land/std@0.203.0/fs/move.ts": "b4f8f46730b40c32ea3c0bc8eb0fd0e8139249a698883c7b3756424cf19785c9", 92 | "https://deno.land/std@0.203.0/fs/walk.ts": "a16146724a6aaf9efdb92023a74e9805195c3469900744ce5de4113b07b29779", 93 | "https://deno.land/std@0.203.0/path/_basename.ts": "057d420c9049821f983f784fd87fa73ac471901fb628920b67972b0f44319343", 94 | "https://deno.land/std@0.203.0/path/_constants.ts": "e49961f6f4f48039c0dfed3c3f93e963ca3d92791c9d478ac5b43183413136e0", 95 | "https://deno.land/std@0.203.0/path/_dirname.ts": "355e297236b2218600aee7a5301b937204c62e12da9db4b0b044993d9e658395", 96 | "https://deno.land/std@0.203.0/path/_extname.ts": "eaaa5aae1acf1f03254d681bd6a8ce42a9cb5b7ff2213a9d4740e8ab31283664", 97 | "https://deno.land/std@0.203.0/path/_format.ts": "4a99270d6810f082e614309164fad75d6f1a483b68eed97c830a506cc589f8b4", 98 | "https://deno.land/std@0.203.0/path/_from_file_url.ts": "6eadfae2e6f63ad9ee46b26db4a1b16583055c0392acedfb50ed2fc694b6f581", 99 | "https://deno.land/std@0.203.0/path/_interface.ts": "6471159dfbbc357e03882c2266d21ef9afdb1e4aa771b0545e90db58a0ba314b", 100 | "https://deno.land/std@0.203.0/path/_is_absolute.ts": "05dac10b5e93c63198b92e3687baa2be178df5321c527dc555266c0f4f51558c", 101 | "https://deno.land/std@0.203.0/path/_join.ts": "815f5e85b042285175b1492dd5781240ce126c23bd97bad6b8211fe7129c538e", 102 | "https://deno.land/std@0.203.0/path/_normalize.ts": "a19ec8706b2707f9dd974662a5cd89fad438e62ab1857e08b314a8eb49a34d81", 103 | "https://deno.land/std@0.203.0/path/_os.ts": "30b0c2875f360c9296dbe6b7f2d528f0f9c741cecad2e97f803f5219e91b40a2", 104 | "https://deno.land/std@0.203.0/path/_parse.ts": "0f9b0ff43682dd9964eb1c4398610c4e165d8db9d3ac9d594220217adf480cfa", 105 | "https://deno.land/std@0.203.0/path/_relative.ts": "27bdeffb5311a47d85be26d37ad1969979359f7636c5cd9fcf05dcd0d5099dc5", 106 | "https://deno.land/std@0.203.0/path/_resolve.ts": "7a3616f1093735ed327e758313b79c3c04ea921808ca5f19ddf240cb68d0adf6", 107 | "https://deno.land/std@0.203.0/path/_to_file_url.ts": "a141e4a525303e1a3a0c0571fd024552b5f3553a2af7d75d1ff3a503dcbb66d8", 108 | "https://deno.land/std@0.203.0/path/_to_namespaced_path.ts": "0d5f4caa2ed98ef7a8786286df6af804b50e38859ae897b5b5b4c8c5930a75c8", 109 | "https://deno.land/std@0.203.0/path/_util.ts": "4e191b1bac6b3bf0c31aab42e5ca2e01a86ab5a0d2e08b75acf8585047a86221", 110 | "https://deno.land/std@0.203.0/path/basename.ts": "bdfa5a624c6a45564dc6758ef2077f2822978a6dbe77b0a3514f7d1f81362930", 111 | "https://deno.land/std@0.203.0/path/common.ts": "ee7505ab01fd22de3963b64e46cff31f40de34f9f8de1fff6a1bd2fe79380000", 112 | "https://deno.land/std@0.203.0/path/dirname.ts": "b6533f4ee4174a526dec50c279534df5345836dfdc15318400b08c62a62a39dd", 113 | "https://deno.land/std@0.203.0/path/extname.ts": "62c4b376300795342fe1e4746c0de518b4dc9c4b0b4617bfee62a2973a9555cf", 114 | "https://deno.land/std@0.203.0/path/format.ts": "110270b238514dd68455a4c54956215a1aff7e37e22e4427b7771cefe1920aa5", 115 | "https://deno.land/std@0.203.0/path/from_file_url.ts": "9f5cb58d58be14c775ec2e57fc70029ac8b17ed3bd7fe93e475b07280adde0ac", 116 | "https://deno.land/std@0.203.0/path/glob.ts": "593e2c3573883225c25c5a21aaa8e9382a696b8e175ea20a3b6a1471ad17aaed", 117 | "https://deno.land/std@0.203.0/path/is_absolute.ts": "0b92eb35a0a8780e9f16f16bb23655b67dace6a8e0d92d42039e518ee38103c1", 118 | "https://deno.land/std@0.203.0/path/join.ts": "31c5419f23d91655b08ec7aec403f4e4cd1a63d39e28f6e42642ea207c2734f8", 119 | "https://deno.land/std@0.203.0/path/mod.ts": "6e1efb0b13121463aedb53ea51dabf5639a3172ab58c89900bbb72b486872532", 120 | "https://deno.land/std@0.203.0/path/normalize.ts": "6ea523e0040979dd7ae2f1be5bf2083941881a252554c0f32566a18b03021955", 121 | "https://deno.land/std@0.203.0/path/parse.ts": "be8de342bb9e1924d78dc4d93c45215c152db7bf738ec32475560424b119b394", 122 | "https://deno.land/std@0.203.0/path/posix.ts": "0a1c1952d132323a88736d03e92bd236f3ed5f9f079e5823fae07c8d978ee61b", 123 | "https://deno.land/std@0.203.0/path/relative.ts": "8bedac226afd360afc45d451a6c29fabceaf32978526bcb38e0c852661f66c61", 124 | "https://deno.land/std@0.203.0/path/resolve.ts": "133161e4949fc97f9ca67988d51376b0f5eef8968a6372325ab84d39d30b80dc", 125 | "https://deno.land/std@0.203.0/path/separator.ts": "40a3e9a4ad10bef23bc2cd6c610291b6c502a06237c2c4cd034a15ca78dedc1f", 126 | "https://deno.land/std@0.203.0/path/to_file_url.ts": "00e6322373dd51ad109956b775e4e72e5f9fa68ce2c6b04e4af2a6eed3825d31", 127 | "https://deno.land/std@0.203.0/path/to_namespaced_path.ts": "1b1db3055c343ab389901adfbda34e82b7386bcd1c744d54f9c1496ee0fd0c3d", 128 | "https://deno.land/std@0.203.0/path/win32.ts": "8b3f80ef7a462511d5e8020ff490edcaa0a0d118f1b1e9da50e2916bdd73f9dd", 129 | "https://deno.land/x/disposable@v1.2.0/mod.ts": "35bdb7afa8506c417a54a81821974f8466d8b7c1d1fb4342c99960a600d838ba", 130 | "https://deno.land/x/disposable@v1.2.0/types.ts": "3533500ccb5234e471b8d65bf5378dd41b5f8f39acccfeb9cdc9e6a407e71734", 131 | "https://deno.land/x/disposable@v1.2.0/using.ts": "800b4681c14a9ba3b4e2865537f710334b25bbfcedbb5b2725108982cf61fad2", 132 | "https://deno.land/x/sandbox@v1.2.0/mod.ts": "46815f3918db8edefb17c36a7d6eb5307da918da9afa7582918caa1da57374bf", 133 | "https://deno.land/x/sandbox@v1.2.0/sandbox.ts": "71d81de3fa0e4bf1c105ef81d20709aefe4ef67878d0451402d6895e887c22ac" 134 | }, 135 | "workspace": { 136 | "dependencies": [ 137 | "jsr:@std/assert@^1.0.5", 138 | "jsr:@std/fs@^1.0.3" 139 | ] 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /mod.ts: -------------------------------------------------------------------------------- 1 | export * from "./sandbox.ts"; 2 | -------------------------------------------------------------------------------- /sandbox.ts: -------------------------------------------------------------------------------- 1 | export type Sandbox = { 2 | /** 3 | * The path to the sandbox directory 4 | */ 5 | readonly path: string; 6 | /** 7 | * The path to the original directory 8 | */ 9 | readonly origin: string; 10 | }; 11 | 12 | /** 13 | * Create a sandbox directory and move into it. 14 | * 15 | * It creates a temporary directory and changes the current working directory 16 | * to it. When disposed, it changes the current working directory back to the 17 | * previous one and removes the temporary directory. 18 | * 19 | * ```ts 20 | * import { sandbox } from "@lambdalisue/sandbox"; 21 | * 22 | * Deno.test("Perform tests in a sandbox directory", async () => { 23 | * // Create a sandbox directory and move into it 24 | * await using sbox = await sandbox(); 25 | * 26 | * // Create a file in the sandbox directory 27 | * await using _f = await Deno.create("foo"); 28 | * 29 | * // Change permission 30 | * await Deno.chmod("foo", 0o700); 31 | * 32 | * // Create a link 33 | * await Deno.link("foo", "bar"); 34 | * 35 | * // Check lstat 36 | * const lstat = await Deno.lstat("foo"); 37 | * 38 | * // etc... 39 | * 40 | * // The sandbox directory is removed and the current directory is restored 41 | * // when the function is finished 42 | * }); 43 | * ``` 44 | * 45 | * Or call `Symbol.asyncDispose` to manually dispose the sandbox directory 46 | * 47 | * ```ts 48 | * import { sandbox } from "@lambdalisue/sandbox"; 49 | * 50 | * Deno.test("Perform tests in a sandbox directory", async () => { 51 | * // Create a sandbox directory and move into it 52 | * const sbox = await sandbox(); 53 | * 54 | * try { 55 | * // Create a file in the sandbox directory 56 | * await using _f = await Deno.create("foo"); 57 | * 58 | * // Change permission 59 | * await Deno.chmod("foo", 0o700); 60 | * 61 | * // Create a link 62 | * await Deno.link("foo", "bar"); 63 | * 64 | * // Check lstat 65 | * const lstat = await Deno.lstat("foo"); 66 | * 67 | * // etc... 68 | * } finally { 69 | * // The sandbox directory is removed and the current directory is restored 70 | * // when 'Symbol.asyncDispose' is invoked 71 | * await sbox[Symbol.asyncDispose](); 72 | * } 73 | * }); 74 | * ``` 75 | */ 76 | export async function sandbox(): Promise { 77 | const path = await Deno.realPath(await Deno.makeTempDir()); 78 | const origin = Deno.cwd(); 79 | try { 80 | Deno.chdir(path); 81 | } catch (err) { 82 | Deno.removeSync(path, { recursive: true }); 83 | throw err; 84 | } 85 | return { 86 | path, 87 | origin, 88 | [Symbol.asyncDispose]: async () => { 89 | Deno.chdir(origin); 90 | try { 91 | await Deno.remove(path, { recursive: true }); 92 | } catch { 93 | // Fail silently 94 | } 95 | }, 96 | }; 97 | } 98 | 99 | /** 100 | * Create a sandbox directory and move into it synchronously. 101 | * 102 | * It creates a temporary directory and changes the current working directory 103 | * to it. When disposed, it changes the current working directory back to the 104 | * previous one and removes the temporary directory. 105 | * 106 | * ```ts 107 | * import { sandboxSync } from "@lambdalisue/sandbox"; 108 | * 109 | * Deno.test("Perform tests in a sandbox directory", () => { 110 | * // Create a sandbox directory and move into it 111 | * using sbox = sandboxSync(); 112 | * 113 | * // Create a file in the sandbox directory 114 | * using _f = Deno.createSync("foo"); 115 | * 116 | * // Change permission 117 | * Deno.chmodSync("foo", 0o700); 118 | * 119 | * // Create a link 120 | * Deno.linkSync("foo", "bar"); 121 | * 122 | * // Check lstat 123 | * const lstat = Deno.lstatSync("foo"); 124 | * 125 | * // etc... 126 | * 127 | * // The sandbox directory is removed and the current directory is restored 128 | * // when the function is finished 129 | * }); 130 | * ``` 131 | * 132 | * Or call `Symbol.dispose` to manually dispose the sandbox directory 133 | * 134 | * ```ts 135 | * import { sandboxSync } from "@lambdalisue/sandbox"; 136 | * 137 | * Deno.test("Perform tests in a sandbox directory", () => { 138 | * // Create a sandbox directory and move into it 139 | * const sbox = sandboxSync(); 140 | * 141 | * try { 142 | * // Create a file in the sandbox directory 143 | * using _f = Deno.createSync("foo"); 144 | * 145 | * // Change permission 146 | * Deno.chmodSync("foo", 0o700); 147 | * 148 | * // Create a link 149 | * Deno.linkSync("foo", "bar"); 150 | * 151 | * // Check lstat 152 | * const lstat = Deno.lstatSync("foo"); 153 | * 154 | * // etc... 155 | * } finally { 156 | * // The sandbox directory is removed and the current directory is restored 157 | * // when 'Symbol.dispose' is invoked 158 | * sbox[Symbol.dispose](); 159 | * } 160 | * }); 161 | * ``` 162 | */ 163 | export function sandboxSync(): Sandbox & Disposable { 164 | const path = Deno.realPathSync(Deno.makeTempDirSync()); 165 | const origin = Deno.cwd(); 166 | try { 167 | Deno.chdir(path); 168 | } catch (err) { 169 | Deno.removeSync(path, { recursive: true }); 170 | throw err; 171 | } 172 | return { 173 | path, 174 | origin, 175 | [Symbol.dispose]: () => { 176 | Deno.chdir(origin); 177 | try { 178 | Deno.removeSync(path, { recursive: true }); 179 | } catch { 180 | // Fail silently 181 | } 182 | }, 183 | }; 184 | } 185 | -------------------------------------------------------------------------------- /sandbox_test.ts: -------------------------------------------------------------------------------- 1 | import { 2 | assert, 3 | assertEquals, 4 | assertNotEquals, 5 | assertRejects, 6 | } from "@std/assert"; 7 | import { existsSync } from "@std/fs/exists"; 8 | import { sandbox, sandboxSync } from "./sandbox.ts"; 9 | 10 | function assertExists(path: string) { 11 | assert(existsSync(path), `${path} must exist`); 12 | } 13 | 14 | function assertNotExists(path: string) { 15 | assert(!existsSync(path), `${path} must not exist`); 16 | } 17 | 18 | Deno.test({ 19 | name: 20 | "sandbox() creates a sandbox directory and that directory is removed when disposed", 21 | fn: async () => { 22 | await using sbox = await sandbox(); 23 | assertExists(sbox.path); 24 | 25 | await sbox[Symbol.asyncDispose](); 26 | assertNotExists(sbox.path); 27 | }, 28 | }); 29 | 30 | Deno.test({ 31 | name: 32 | "sandbox() changes the current working directory to the sandbox directory and back when disposed", 33 | fn: async () => { 34 | const cwd = () => Deno.realPathSync(Deno.cwd()); 35 | await using sbox = await sandbox(); 36 | assertEquals(cwd(), sbox.path); 37 | assertNotEquals(cwd(), sbox.origin); 38 | 39 | await sbox[Symbol.asyncDispose](); 40 | assertNotEquals(cwd(), sbox.path); 41 | assertEquals(cwd(), sbox.origin); 42 | }, 43 | }); 44 | 45 | Deno.test({ 46 | name: 47 | "sandboxSync() creates a sandbox directory and that directory is removed when disposed", 48 | fn: () => { 49 | using sbox = sandboxSync(); 50 | assertExists(sbox.path); 51 | 52 | sbox[Symbol.dispose](); 53 | assertNotExists(sbox.path); 54 | }, 55 | }); 56 | 57 | Deno.test({ 58 | name: 59 | "sandboxSync() changes the current working directory to the sandbox directory and back when disposed", 60 | fn: () => { 61 | const cwd = () => Deno.realPathSync(Deno.cwd()); 62 | using sbox = sandboxSync(); 63 | assertEquals(cwd(), sbox.path); 64 | assertNotEquals(cwd(), sbox.origin); 65 | 66 | sbox[Symbol.dispose](); 67 | assertNotEquals(cwd(), sbox.path); 68 | assertEquals(cwd(), sbox.origin); 69 | }, 70 | }); 71 | 72 | Deno.test({ 73 | name: "Deno.create() performs its operation in a sandbox directory", 74 | fn: async () => { 75 | await using _sbox = await sandbox(); 76 | await using _f = await Deno.create("foo"); 77 | assertExists("foo"); 78 | }, 79 | }); 80 | 81 | Deno.test({ 82 | ignore: Deno.build.os == "windows", 83 | name: "Deno.lstat() performs its operation in a sandbox directory", 84 | fn: async () => { 85 | await using _sbox = await sandbox(); 86 | await using _f = await Deno.create("foo"); 87 | await Deno.lstat("foo"); 88 | await assertRejects(() => Deno.lstat("bar"), Deno.errors.NotFound); 89 | }, 90 | }); 91 | 92 | Deno.test({ 93 | ignore: Deno.build.os == "windows", 94 | name: "Deno.chmod() performs its operation in a sandbox directory", 95 | fn: async () => { 96 | await using _sbox = await sandbox(); 97 | await using _f = await Deno.create("foo"); 98 | await Deno.chmod("foo", 0o700); 99 | const s = await Deno.lstat("foo"); 100 | assertEquals((s.mode ?? 0) & 0o777, 0o700); 101 | }, 102 | }); 103 | 104 | Deno.test({ 105 | ignore: true, // This test requires a root privilege 106 | name: "Deno.chown() performs its operation in a sandbox directory", 107 | fn: async () => { 108 | const uid = 10000; 109 | const gid = 10000; 110 | await using _sbox = await sandbox(); 111 | await using _f = await Deno.create("foo"); 112 | await Deno.chown("foo", uid, gid); 113 | const s = await Deno.lstat("foo"); 114 | assertEquals(s.uid, uid); 115 | assertEquals(s.gid, gid); 116 | }, 117 | }); 118 | 119 | Deno.test({ 120 | name: "Deno.copyFile() performs its operation in a sandbox directory", 121 | fn: async () => { 122 | await using _sbox = await sandbox(); 123 | await using _f = await Deno.create("foo"); 124 | await Deno.copyFile("foo", "bar"); 125 | assertExists("foo"); 126 | assertExists("bar"); 127 | }, 128 | }); 129 | 130 | Deno.test({ 131 | ignore: Deno.build.os == "windows", 132 | name: "Deno.link() performs its operation in a sandbox directory", 133 | fn: async () => { 134 | await using _sbox = await sandbox(); 135 | await using _f = await Deno.create("foo"); 136 | await Deno.link("foo", "bar"); 137 | assertExists("foo"); 138 | assertExists("bar"); 139 | const s = await Deno.lstat("bar"); 140 | assertEquals(s.isFile, true); 141 | assertEquals(s.nlink, 2); 142 | }, 143 | }); 144 | 145 | Deno.test({ 146 | name: "Deno.mkdir() performs its operation in a sandbox directory", 147 | fn: async () => { 148 | await using _sbox = await sandbox(); 149 | await Deno.mkdir("foo"); 150 | assertExists("foo"); 151 | const s = await Deno.lstat("foo"); 152 | assertEquals(s.isDirectory, true); 153 | }, 154 | }); 155 | 156 | Deno.test({ 157 | name: "Deno.open() performs its operation in a sandbox directory", 158 | fn: async () => { 159 | await using _sbox = await sandbox(); 160 | await using _f = await Deno.create("foo"); 161 | await using f = await Deno.open("foo"); 162 | const s = await f.stat(); 163 | assertEquals(s.isFile, true); 164 | }, 165 | }); 166 | 167 | Deno.test({ 168 | name: "Deno.readDir() performs its operation in a sandbox directory", 169 | fn: async () => { 170 | await using _sbox = await sandbox(); 171 | await using _a = await Deno.create("alpha"); 172 | await using _b = await Deno.create("beta"); 173 | await using _c = await Deno.create("gamma"); 174 | const items: Deno.DirEntry[] = []; 175 | for await (const item of Deno.readDir(".")) { 176 | items.push(item); 177 | } 178 | assertEquals(items.sort((a, b) => a.name.localeCompare(b.name)), [ 179 | { 180 | isDirectory: false, 181 | isFile: true, 182 | isSymlink: false, 183 | name: "alpha", 184 | }, 185 | { 186 | isDirectory: false, 187 | isFile: true, 188 | isSymlink: false, 189 | name: "beta", 190 | }, 191 | { 192 | isDirectory: false, 193 | isFile: true, 194 | isSymlink: false, 195 | name: "gamma", 196 | }, 197 | ]); 198 | }, 199 | }); 200 | 201 | Deno.test({ 202 | name: "Deno.readFile() performs its operation in a sandbox directory", 203 | fn: async () => { 204 | await using _sbox = await sandbox(); 205 | await using _f = await Deno.create("foo"); 206 | await Deno.writeFile("foo", new Uint8Array([0, 1, 2])); 207 | const content = await Deno.readFile("foo"); 208 | assertEquals(content, new Uint8Array([0, 1, 2])); 209 | }, 210 | }); 211 | 212 | Deno.test({ 213 | name: "Deno.readLink() performs its operation in a sandbox directory", 214 | fn: async () => { 215 | await using _sbox = await sandbox(); 216 | await using _f = await Deno.create("foo"); 217 | await Deno.symlink("foo", "bar"); 218 | assertEquals(await Deno.readLink("bar"), "foo"); 219 | }, 220 | }); 221 | 222 | Deno.test({ 223 | name: "Deno.readTextFile() performs its operation in a sandbox directory", 224 | fn: async () => { 225 | await using _sbox = await sandbox(); 226 | await using _f = await Deno.create("foo"); 227 | await Deno.writeTextFile("foo", "Hello"); 228 | const content = await Deno.readTextFile("foo"); 229 | assertEquals(content, "Hello"); 230 | }, 231 | }); 232 | 233 | Deno.test({ 234 | name: "Deno.realPath() performs its operation in a sandbox directory", 235 | fn: async () => { 236 | await using _sbox = await sandbox(); 237 | await using _f = await Deno.create("foo"); 238 | await Deno.symlink("foo", "bar"); 239 | assertEquals( 240 | await Deno.realPath("foo"), 241 | await Deno.realPath("bar"), 242 | ); 243 | }, 244 | }); 245 | 246 | Deno.test({ 247 | name: "Deno.remove() performs its operation in a sandbox directory", 248 | fn: async () => { 249 | await using _sbox = await sandbox(); 250 | await using _f = await Deno.create("foo"); 251 | await Deno.remove("foo"); 252 | assertNotExists("foo"); 253 | }, 254 | }); 255 | 256 | Deno.test({ 257 | name: "Deno.rename() performs its operation in a sandbox directory", 258 | fn: async () => { 259 | await using _sbox = await sandbox(); 260 | await using _f = await Deno.create("foo"); 261 | await Deno.rename("foo", "bar"); 262 | assertNotExists("foo"); 263 | assertExists("bar"); 264 | const s = await Deno.lstat("bar"); 265 | assertEquals(s.isFile, true); 266 | }, 267 | }); 268 | 269 | Deno.test({ 270 | name: "Deno.stat() performs its operation in a sandbox directory", 271 | fn: async () => { 272 | await using _sbox = await sandbox(); 273 | await using _f = await Deno.create("foo"); 274 | const s = await Deno.stat("foo"); 275 | assertEquals(s.isFile, true); 276 | }, 277 | }); 278 | 279 | Deno.test({ 280 | name: "Deno.symlink() performs its operation in a sandbox directory", 281 | fn: async () => { 282 | await using _sbox = await sandbox(); 283 | await using _f = await Deno.create("foo"); 284 | await Deno.symlink("foo", "bar"); 285 | const s = await Deno.lstat("bar"); 286 | assertEquals(s.isSymlink, true); 287 | }, 288 | }); 289 | 290 | Deno.test({ 291 | name: "Deno.writeFile() performs its operation in a sandbox directory", 292 | fn: async () => { 293 | await using _sbox = await sandbox(); 294 | await using _f = await Deno.create("foo"); 295 | await Deno.writeFile("foo", new Uint8Array([0, 1, 2])); 296 | const content = await Deno.readFile("foo"); 297 | assertEquals(content, new Uint8Array([0, 1, 2])); 298 | }, 299 | }); 300 | 301 | Deno.test({ 302 | name: "Deno.writeTextFile() performs its operation in a sandbox directory", 303 | fn: async () => { 304 | await using _sbox = await sandbox(); 305 | await using _f = await Deno.create("foo"); 306 | await Deno.writeTextFile("foo", "Hello"); 307 | const content = await Deno.readTextFile("foo"); 308 | assertEquals(content, "Hello"); 309 | }, 310 | }); 311 | --------------------------------------------------------------------------------