readme.md (6907B)
1 # escalade [](https://github.com/lukeed/escalade/actions) [](https://codecov.io/gh/lukeed/escalade) 2 3 > A tiny (183B to 210B) and [fast](#benchmarks) utility to ascend parent directories 4 5 With [escalade](https://en.wikipedia.org/wiki/Escalade), you can scale parent directories until you've found what you're looking for.<br>Given an input file or directory, `escalade` will continue executing your callback function until either: 6 7 1) the callback returns a truthy value 8 2) `escalade` has reached the system root directory (eg, `/`) 9 10 > **Important:**<br>Please note that `escalade` only deals with direct ancestry – it will not dive into parents' sibling directories. 11 12 --- 13 14 **Notice:** As of v3.1.0, `escalade` now includes [Deno support](http://deno.land/x/escalade)! Please see [Deno Usage](#deno) below. 15 16 --- 17 18 ## Install 19 20 ``` 21 $ npm install --save escalade 22 ``` 23 24 25 ## Modes 26 27 There are two "versions" of `escalade` available: 28 29 #### "async" 30 > **Node.js:** >= 8.x<br> 31 > **Size (gzip):** 210 bytes<br> 32 > **Availability:** [CommonJS](https://unpkg.com/escalade/dist/index.js), [ES Module](https://unpkg.com/escalade/dist/index.mjs) 33 34 This is the primary/default mode. It makes use of `async`/`await` and [`util.promisify`](https://nodejs.org/api/util.html#util_util_promisify_original). 35 36 #### "sync" 37 > **Node.js:** >= 6.x<br> 38 > **Size (gzip):** 183 bytes<br> 39 > **Availability:** [CommonJS](https://unpkg.com/escalade/sync/index.js), [ES Module](https://unpkg.com/escalade/sync/index.mjs) 40 41 This is the opt-in mode, ideal for scenarios where `async` usage cannot be supported. 42 43 44 ## Usage 45 46 ***Example Structure*** 47 48 ``` 49 /Users/lukeed 50 └── oss 51 ├── license 52 └── escalade 53 ├── package.json 54 └── test 55 └── fixtures 56 ├── index.js 57 └── foobar 58 └── demo.js 59 ``` 60 61 ***Example Usage*** 62 63 ```js 64 //~> demo.js 65 import { join } from 'path'; 66 import escalade from 'escalade'; 67 68 const input = join(__dirname, 'demo.js'); 69 // or: const input = __dirname; 70 71 const pkg = await escalade(input, (dir, names) => { 72 console.log('~> dir:', dir); 73 console.log('~> names:', names); 74 console.log('---'); 75 76 if (names.includes('package.json')) { 77 // will be resolved into absolute 78 return 'package.json'; 79 } 80 }); 81 82 //~> dir: /Users/lukeed/oss/escalade/test/fixtures/foobar 83 //~> names: ['demo.js'] 84 //--- 85 //~> dir: /Users/lukeed/oss/escalade/test/fixtures 86 //~> names: ['index.js', 'foobar'] 87 //--- 88 //~> dir: /Users/lukeed/oss/escalade/test 89 //~> names: ['fixtures'] 90 //--- 91 //~> dir: /Users/lukeed/oss/escalade 92 //~> names: ['package.json', 'test'] 93 //--- 94 95 console.log(pkg); 96 //=> /Users/lukeed/oss/escalade/package.json 97 98 // Now search for "missing123.txt" 99 // (Assume it doesn't exist anywhere!) 100 const missing = await escalade(input, (dir, names) => { 101 console.log('~> dir:', dir); 102 return names.includes('missing123.txt') && 'missing123.txt'; 103 }); 104 105 //~> dir: /Users/lukeed/oss/escalade/test/fixtures/foobar 106 //~> dir: /Users/lukeed/oss/escalade/test/fixtures 107 //~> dir: /Users/lukeed/oss/escalade/test 108 //~> dir: /Users/lukeed/oss/escalade 109 //~> dir: /Users/lukeed/oss 110 //~> dir: /Users/lukeed 111 //~> dir: /Users 112 //~> dir: / 113 114 console.log(missing); 115 //=> undefined 116 ``` 117 118 > **Note:** To run the above example with "sync" mode, import from `escalade/sync` and remove the `await` keyword. 119 120 121 ## API 122 123 ### escalade(input, callback) 124 Returns: `string|void` or `Promise<string|void>` 125 126 When your `callback` locates a file, `escalade` will resolve/return with an absolute path.<br> 127 If your `callback` was never satisfied, then `escalade` will resolve/return with nothing (undefined). 128 129 > **Important:**<br>The `sync` and `async` versions share the same API.<br>The **only** difference is that `sync` is not Promise-based. 130 131 #### input 132 Type: `string` 133 134 The path from which to start ascending. 135 136 This may be a file or a directory path.<br>However, when `input` is a file, `escalade` will begin with its parent directory. 137 138 > **Important:** Unless given an absolute path, `input` will be resolved from `process.cwd()` location. 139 140 #### callback 141 Type: `Function` 142 143 The callback to execute for each ancestry level. It always is given two arguments: 144 145 1) `dir` - an absolute path of the current parent directory 146 2) `names` - a list (`string[]`) of contents _relative to_ the `dir` parent 147 148 > **Note:** The `names` list can contain names of files _and_ directories. 149 150 When your callback returns a _falsey_ value, then `escalade` will continue with `dir`'s parent directory, re-invoking your callback with new argument values. 151 152 When your callback returns a string, then `escalade` stops iteration immediately.<br> 153 If the string is an absolute path, then it's left as is. Otherwise, the string is resolved into an absolute path _from_ the `dir` that housed the satisfying condition. 154 155 > **Important:** Your `callback` can be a `Promise/AsyncFunction` when using the "async" version of `escalade`. 156 157 ## Benchmarks 158 159 > Running on Node.js v10.13.0 160 161 ``` 162 # Load Time 163 find-up 3.891ms 164 escalade 0.485ms 165 escalade/sync 0.309ms 166 167 # Levels: 6 (target = "foo.txt"): 168 find-up x 24,856 ops/sec ±6.46% (55 runs sampled) 169 escalade x 73,084 ops/sec ±4.23% (73 runs sampled) 170 find-up.sync x 3,663 ops/sec ±1.12% (83 runs sampled) 171 escalade/sync x 9,360 ops/sec ±0.62% (88 runs sampled) 172 173 # Levels: 12 (target = "package.json"): 174 find-up x 29,300 ops/sec ±10.68% (70 runs sampled) 175 escalade x 73,685 ops/sec ± 5.66% (66 runs sampled) 176 find-up.sync x 1,707 ops/sec ± 0.58% (91 runs sampled) 177 escalade/sync x 4,667 ops/sec ± 0.68% (94 runs sampled) 178 179 # Levels: 18 (target = "missing123.txt"): 180 find-up x 21,818 ops/sec ±17.37% (14 runs sampled) 181 escalade x 67,101 ops/sec ±21.60% (20 runs sampled) 182 find-up.sync x 1,037 ops/sec ± 2.86% (88 runs sampled) 183 escalade/sync x 1,248 ops/sec ± 0.50% (93 runs sampled) 184 ``` 185 186 ## Deno 187 188 As of v3.1.0, `escalade` is available on the Deno registry. 189 190 Please note that the [API](#api) is identical and that there are still [two modes](#modes) from which to choose: 191 192 ```ts 193 // Choose "async" mode 194 import escalade from 'https://deno.land/escalade/async.ts'; 195 196 // Choose "sync" mode 197 import escalade from 'https://deno.land/escalade/sync.ts'; 198 ``` 199 200 > **Important:** The `allow-read` permission is required! 201 202 203 ## Related 204 205 - [premove](https://github.com/lukeed/premove) - A tiny (247B) utility to remove items recursively 206 - [totalist](https://github.com/lukeed/totalist) - A tiny (195B to 224B) utility to recursively list all (total) files in a directory 207 - [mk-dirs](https://github.com/lukeed/mk-dirs) - A tiny (420B) utility to make a directory and its parents, recursively 208 209 ## License 210 211 MIT © [Luke Edwards](https://lukeed.com)