├── .github └── workflows │ ├── ci.yml │ ├── commit-if-modified.sh │ ├── copyright-year.sh │ ├── isaacs-makework.yml │ └── package-json-repo.js ├── .gitignore ├── .travis.yml ├── LICENSE ├── README.md ├── index.js ├── old.js ├── package-lock.json ├── package.json └── test ├── monkeypatching.js └── symlinks.js /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: [push, pull_request] 4 | 5 | jobs: 6 | build: 7 | strategy: 8 | matrix: 9 | node-version: [12.x, 14.x, 16.x, 17.x] 10 | platform: 11 | - os: ubuntu-latest 12 | shell: bash 13 | - os: macos-latest 14 | shell: bash 15 | - os: windows-latest 16 | shell: bash 17 | - os: windows-latest 18 | shell: powershell 19 | fail-fast: false 20 | 21 | runs-on: ${{ matrix.platform.os }} 22 | defaults: 23 | run: 24 | shell: ${{ matrix.platform.shell }} 25 | 26 | steps: 27 | - name: Checkout Repository 28 | uses: actions/checkout@v1.1.0 29 | 30 | - name: Use Nodejs ${{ matrix.node-version }} 31 | uses: actions/setup-node@v1 32 | with: 33 | node-version: ${{ matrix.node-version }} 34 | 35 | - name: Install dependencies 36 | run: npm install 37 | 38 | - name: Run Tests 39 | run: npm test -- -c -t0 40 | -------------------------------------------------------------------------------- /.github/workflows/commit-if-modified.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | git config --global user.email "$1" 3 | shift 4 | git config --global user.name "$1" 5 | shift 6 | message="$1" 7 | shift 8 | if [ $(git status --porcelain "$@" | egrep '^ M' | wc -l) -gt 0 ]; then 9 | git add "$@" 10 | git commit -m "$message" 11 | git push || git pull --rebase 12 | git push 13 | fi 14 | -------------------------------------------------------------------------------- /.github/workflows/copyright-year.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | dir=${1:-$PWD} 3 | dates=($(git log --date=format:%Y --pretty=format:'%ad' --reverse | sort | uniq)) 4 | if [ "${#dates[@]}" -eq 1 ]; then 5 | datestr="${dates}" 6 | else 7 | datestr="${dates}-${dates[${#dates[@]}-1]}" 8 | fi 9 | 10 | stripDate='s/^((.*)Copyright\b(.*?))((?:,\s*)?(([0-9]{4}\s*-\s*[0-9]{4})|(([0-9]{4},\s*)*[0-9]{4})))(?:,)?\s*(.*)\n$/$1$9\n/g' 11 | addDate='s/^.*Copyright(?:\s*\(c\))? /Copyright \(c\) '$datestr' /g' 12 | for l in $dir/LICENSE*; do 13 | perl -pi -e "$stripDate" $l 14 | perl -pi -e "$addDate" $l 15 | done 16 | -------------------------------------------------------------------------------- /.github/workflows/isaacs-makework.yml: -------------------------------------------------------------------------------- 1 | name: "various tidying up tasks to silence nagging" 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | workflow_dispatch: 8 | 9 | jobs: 10 | makework: 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v2 14 | with: 15 | fetch-depth: 0 16 | - name: Use Node.js 17 | uses: actions/setup-node@v2.1.4 18 | with: 19 | node-version: 16.x 20 | - name: put repo in package.json 21 | run: node .github/workflows/package-json-repo.js 22 | - name: check in package.json if modified 23 | run: | 24 | bash -x .github/workflows/commit-if-modified.sh \ 25 | "package-json-repo-bot@example.com" \ 26 | "package.json Repo Bot" \ 27 | "chore: add repo to package.json" \ 28 | package.json package-lock.json 29 | - name: put all dates in license copyright line 30 | run: bash .github/workflows/copyright-year.sh 31 | - name: check in licenses if modified 32 | run: | 33 | bash .github/workflows/commit-if-modified.sh \ 34 | "license-year-bot@example.com" \ 35 | "License Year Bot" \ 36 | "chore: add copyright year to license" \ 37 | LICENSE* 38 | -------------------------------------------------------------------------------- /.github/workflows/package-json-repo.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | const pf = require.resolve(`${process.cwd()}/package.json`) 4 | const pj = require(pf) 5 | 6 | if (!pj.repository && process.env.GITHUB_REPOSITORY) { 7 | const fs = require('fs') 8 | const server = process.env.GITHUB_SERVER_URL || 'https://github.com' 9 | const repo = `${server}/${process.env.GITHUB_REPOSITORY}` 10 | pj.repository = repo 11 | const json = fs.readFileSync(pf, 'utf8') 12 | const match = json.match(/^\s*\{[\r\n]+([ \t]*)"/) 13 | const indent = match[1] 14 | const output = JSON.stringify(pj, null, indent || 2) + '\n' 15 | fs.writeFileSync(pf, output) 16 | } 17 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | coverage/ 2 | .nyc_output 3 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | sudo: false 2 | language: node_js 3 | node_js: 4 | - '0.10' 5 | - '0.12' 6 | - '4' 7 | - '5' 8 | - '6' 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The ISC License 2 | 3 | Copyright (c) 2016-2023 Isaac Z. Schlueter and Contributors 4 | 5 | Permission to use, copy, modify, and/or distribute this software for any 6 | purpose with or without fee is hereby granted, provided that the above 7 | copyright notice and this permission notice appear in all copies. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 10 | WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 11 | MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 12 | ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 13 | WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 14 | ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR 15 | IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 16 | 17 | ---- 18 | 19 | This library bundles a version of the `fs.realpath` and `fs.realpathSync` 20 | methods from Node.js v0.10 under the terms of the Node.js MIT license. 21 | 22 | Node's license follows, also included at the header of `old.js` which contains 23 | the licensed code: 24 | 25 | Copyright (c) 2016-2023 Joyent, Inc. and other Node contributors. 26 | 27 | Permission is hereby granted, free of charge, to any person obtaining a 28 | copy of this software and associated documentation files (the "Software"), 29 | to deal in the Software without restriction, including without limitation 30 | the rights to use, copy, modify, merge, publish, distribute, sublicense, 31 | and/or sell copies of the Software, and to permit persons to whom the 32 | Software is furnished to do so, subject to the following conditions: 33 | 34 | The above copyright notice and this permission notice shall be included in 35 | all copies or substantial portions of the Software. 36 | 37 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 38 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 39 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 40 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 41 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING 42 | FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER 43 | DEALINGS IN THE SOFTWARE. 44 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # fs.realpath 2 | 3 | A backwards-compatible fs.realpath for Node v6 and above 4 | 5 | In Node v6, the JavaScript implementation of fs.realpath was replaced 6 | with a faster (but less resilient) native implementation. That raises 7 | new and platform-specific errors and cannot handle long or excessively 8 | symlink-looping paths. 9 | 10 | This module handles those cases by detecting the new errors and 11 | falling back to the JavaScript implementation. On versions of Node 12 | prior to v6, it has no effect. 13 | 14 | ## USAGE 15 | 16 | ```js 17 | var rp = require('fs.realpath') 18 | 19 | // async version 20 | rp.realpath(someLongAndLoopingPath, function (er, real) { 21 | // the ELOOP was handled, but it was a bit slower 22 | }) 23 | 24 | // sync version 25 | var real = rp.realpathSync(someLongAndLoopingPath) 26 | 27 | // monkeypatch at your own risk! 28 | // This replaces the fs.realpath/fs.realpathSync builtins 29 | rp.monkeypatch() 30 | 31 | // un-do the monkeypatching 32 | rp.unmonkeypatch() 33 | ``` 34 | -------------------------------------------------------------------------------- /index.js: -------------------------------------------------------------------------------- 1 | module.exports = realpath 2 | realpath.realpath = realpath 3 | realpath.sync = realpathSync 4 | realpath.realpathSync = realpathSync 5 | realpath.monkeypatch = monkeypatch 6 | realpath.unmonkeypatch = unmonkeypatch 7 | 8 | var fs = require('fs') 9 | var origRealpath = fs.realpath 10 | var origRealpathSync = fs.realpathSync 11 | 12 | var version = process.version 13 | var ok = /^v[0-5]\./.test(version) 14 | var old = require('./old.js') 15 | 16 | function newError (er) { 17 | return er && er.syscall === 'realpath' && ( 18 | er.code === 'ELOOP' || 19 | er.code === 'ENOMEM' || 20 | er.code === 'ENAMETOOLONG' 21 | ) 22 | } 23 | 24 | function realpath (p, cache, cb) { 25 | if (ok) { 26 | return origRealpath(p, cache, cb) 27 | } 28 | 29 | if (typeof cache === 'function') { 30 | cb = cache 31 | cache = null 32 | } 33 | origRealpath(p, cache, function (er, result) { 34 | if (newError(er)) { 35 | old.realpath(p, cache, cb) 36 | } else { 37 | cb(er, result) 38 | } 39 | }) 40 | } 41 | 42 | function realpathSync (p, cache) { 43 | if (ok) { 44 | return origRealpathSync(p, cache) 45 | } 46 | 47 | try { 48 | return origRealpathSync(p, cache) 49 | } catch (er) { 50 | if (newError(er)) { 51 | return old.realpathSync(p, cache) 52 | } else { 53 | throw er 54 | } 55 | } 56 | } 57 | 58 | function monkeypatch () { 59 | fs.realpath = realpath 60 | fs.realpathSync = realpathSync 61 | } 62 | 63 | function unmonkeypatch () { 64 | fs.realpath = origRealpath 65 | fs.realpathSync = origRealpathSync 66 | } 67 | -------------------------------------------------------------------------------- /old.js: -------------------------------------------------------------------------------- 1 | /* istanbul ignore file - tested in node */ 2 | // Copyright Joyent, Inc. and other Node contributors. 3 | // 4 | // Permission is hereby granted, free of charge, to any person obtaining a 5 | // copy of this software and associated documentation files (the 6 | // "Software"), to deal in the Software without restriction, including 7 | // without limitation the rights to use, copy, modify, merge, publish, 8 | // distribute, sublicense, and/or sell copies of the Software, and to permit 9 | // persons to whom the Software is furnished to do so, subject to the 10 | // following conditions: 11 | // 12 | // The above copyright notice and this permission notice shall be included 13 | // in all copies or substantial portions of the Software. 14 | // 15 | // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS 16 | // OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 17 | // MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN 18 | // NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, 19 | // DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR 20 | // OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE 21 | // USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | 23 | var pathModule = require('path'); 24 | var isWindows = process.platform === 'win32'; 25 | var fs = require('fs'); 26 | 27 | // JavaScript implementation of realpath, ported from node pre-v6 28 | 29 | var DEBUG = process.env.NODE_DEBUG && /fs/.test(process.env.NODE_DEBUG); 30 | 31 | function rethrow() { 32 | // Only enable in debug mode. A backtrace uses ~1000 bytes of heap space and 33 | // is fairly slow to generate. 34 | var callback; 35 | if (DEBUG) { 36 | var backtrace = new Error; 37 | callback = debugCallback; 38 | } else 39 | callback = missingCallback; 40 | 41 | return callback; 42 | 43 | function debugCallback(err) { 44 | if (err) { 45 | backtrace.message = err.message; 46 | err = backtrace; 47 | missingCallback(err); 48 | } 49 | } 50 | 51 | function missingCallback(err) { 52 | if (err) { 53 | if (process.throwDeprecation) 54 | throw err; // Forgot a callback but don't know where? Use NODE_DEBUG=fs 55 | else if (!process.noDeprecation) { 56 | var msg = 'fs: missing callback ' + (err.stack || err.message); 57 | if (process.traceDeprecation) 58 | console.trace(msg); 59 | else 60 | console.error(msg); 61 | } 62 | } 63 | } 64 | } 65 | 66 | function maybeCallback(cb) { 67 | return typeof cb === 'function' ? cb : rethrow(); 68 | } 69 | 70 | var normalize = pathModule.normalize; 71 | 72 | // Regexp that finds the next partion of a (partial) path 73 | // result is [base_with_slash, base], e.g. ['somedir/', 'somedir'] 74 | if (isWindows) { 75 | var nextPartRe = /(.*?)(?:[\/\\]+|$)/g; 76 | } else { 77 | var nextPartRe = /(.*?)(?:[\/]+|$)/g; 78 | } 79 | 80 | // Regex to find the device root, including trailing slash. E.g. 'c:\\'. 81 | if (isWindows) { 82 | var splitRootRe = /^(?:[a-zA-Z]:|[\\\/]{2}[^\\\/]+[\\\/][^\\\/]+)?[\\\/]*/; 83 | } else { 84 | var splitRootRe = /^[\/]*/; 85 | } 86 | 87 | exports.realpathSync = function realpathSync(p, cache) { 88 | // make p is absolute 89 | p = pathModule.resolve(p); 90 | 91 | if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { 92 | return cache[p]; 93 | } 94 | 95 | var original = p, 96 | seenLinks = {}, 97 | knownHard = {}; 98 | 99 | // current character position in p 100 | var pos; 101 | // the partial path so far, including a trailing slash if any 102 | var current; 103 | // the partial path without a trailing slash (except when pointing at a root) 104 | var base; 105 | // the partial path scanned in the previous round, with slash 106 | var previous; 107 | 108 | start(); 109 | 110 | function start() { 111 | // Skip over roots 112 | var m = splitRootRe.exec(p); 113 | pos = m[0].length; 114 | current = m[0]; 115 | base = m[0]; 116 | previous = ''; 117 | 118 | // On windows, check that the root exists. On unix there is no need. 119 | if (isWindows && !knownHard[base]) { 120 | fs.lstatSync(base); 121 | knownHard[base] = true; 122 | } 123 | } 124 | 125 | // walk down the path, swapping out linked pathparts for their real 126 | // values 127 | // NB: p.length changes. 128 | while (pos < p.length) { 129 | // find the next part 130 | nextPartRe.lastIndex = pos; 131 | var result = nextPartRe.exec(p); 132 | previous = current; 133 | current += result[0]; 134 | base = previous + result[1]; 135 | pos = nextPartRe.lastIndex; 136 | 137 | // continue if not a symlink 138 | if (knownHard[base] || (cache && cache[base] === base)) { 139 | continue; 140 | } 141 | 142 | var resolvedLink; 143 | if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { 144 | // some known symbolic link. no need to stat again. 145 | resolvedLink = cache[base]; 146 | } else { 147 | var stat = fs.lstatSync(base); 148 | if (!stat.isSymbolicLink()) { 149 | knownHard[base] = true; 150 | if (cache) cache[base] = base; 151 | continue; 152 | } 153 | 154 | // read the link if it wasn't read before 155 | // dev/ino always return 0 on windows, so skip the check. 156 | var linkTarget = null; 157 | if (!isWindows) { 158 | var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); 159 | if (seenLinks.hasOwnProperty(id)) { 160 | linkTarget = seenLinks[id]; 161 | } 162 | } 163 | if (linkTarget === null) { 164 | fs.statSync(base); 165 | linkTarget = fs.readlinkSync(base); 166 | } 167 | resolvedLink = pathModule.resolve(previous, linkTarget); 168 | // track this, if given a cache. 169 | if (cache) cache[base] = resolvedLink; 170 | if (!isWindows) seenLinks[id] = linkTarget; 171 | } 172 | 173 | // resolve the link, then start over 174 | p = pathModule.resolve(resolvedLink, p.slice(pos)); 175 | start(); 176 | } 177 | 178 | if (cache) cache[original] = p; 179 | 180 | return p; 181 | }; 182 | 183 | 184 | exports.realpath = function realpath(p, cache, cb) { 185 | if (typeof cb !== 'function') { 186 | cb = maybeCallback(cache); 187 | cache = null; 188 | } 189 | 190 | // make p is absolute 191 | p = pathModule.resolve(p); 192 | 193 | if (cache && Object.prototype.hasOwnProperty.call(cache, p)) { 194 | return process.nextTick(cb.bind(null, null, cache[p])); 195 | } 196 | 197 | var original = p, 198 | seenLinks = {}, 199 | knownHard = {}; 200 | 201 | // current character position in p 202 | var pos; 203 | // the partial path so far, including a trailing slash if any 204 | var current; 205 | // the partial path without a trailing slash (except when pointing at a root) 206 | var base; 207 | // the partial path scanned in the previous round, with slash 208 | var previous; 209 | 210 | start(); 211 | 212 | function start() { 213 | // Skip over roots 214 | var m = splitRootRe.exec(p); 215 | pos = m[0].length; 216 | current = m[0]; 217 | base = m[0]; 218 | previous = ''; 219 | 220 | // On windows, check that the root exists. On unix there is no need. 221 | if (isWindows && !knownHard[base]) { 222 | fs.lstat(base, function(err) { 223 | if (err) return cb(err); 224 | knownHard[base] = true; 225 | LOOP(); 226 | }); 227 | } else { 228 | process.nextTick(LOOP); 229 | } 230 | } 231 | 232 | // walk down the path, swapping out linked pathparts for their real 233 | // values 234 | function LOOP() { 235 | // stop if scanned past end of path 236 | if (pos >= p.length) { 237 | if (cache) cache[original] = p; 238 | return cb(null, p); 239 | } 240 | 241 | // find the next part 242 | nextPartRe.lastIndex = pos; 243 | var result = nextPartRe.exec(p); 244 | previous = current; 245 | current += result[0]; 246 | base = previous + result[1]; 247 | pos = nextPartRe.lastIndex; 248 | 249 | // continue if not a symlink 250 | if (knownHard[base] || (cache && cache[base] === base)) { 251 | return process.nextTick(LOOP); 252 | } 253 | 254 | if (cache && Object.prototype.hasOwnProperty.call(cache, base)) { 255 | // known symbolic link. no need to stat again. 256 | return gotResolvedLink(cache[base]); 257 | } 258 | 259 | return fs.lstat(base, gotStat); 260 | } 261 | 262 | function gotStat(err, stat) { 263 | if (err) return cb(err); 264 | 265 | // if not a symlink, skip to the next path part 266 | if (!stat.isSymbolicLink()) { 267 | knownHard[base] = true; 268 | if (cache) cache[base] = base; 269 | return process.nextTick(LOOP); 270 | } 271 | 272 | // stat & read the link if not read before 273 | // call gotTarget as soon as the link target is known 274 | // dev/ino always return 0 on windows, so skip the check. 275 | if (!isWindows) { 276 | var id = stat.dev.toString(32) + ':' + stat.ino.toString(32); 277 | if (seenLinks.hasOwnProperty(id)) { 278 | return gotTarget(null, seenLinks[id], base); 279 | } 280 | } 281 | fs.stat(base, function(err) { 282 | if (err) return cb(err); 283 | 284 | fs.readlink(base, function(err, target) { 285 | if (!isWindows) seenLinks[id] = target; 286 | gotTarget(err, target); 287 | }); 288 | }); 289 | } 290 | 291 | function gotTarget(err, target, base) { 292 | if (err) return cb(err); 293 | 294 | var resolvedLink = pathModule.resolve(previous, target); 295 | if (cache) cache[base] = resolvedLink; 296 | gotResolvedLink(resolvedLink); 297 | } 298 | 299 | function gotResolvedLink(resolvedLink) { 300 | // resolve the link, then start over 301 | p = pathModule.resolve(resolvedLink, p.slice(pos)); 302 | start(); 303 | } 304 | }; 305 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "fs.realpath", 3 | "version": "1.0.0", 4 | "description": "Use node's fs.realpath, but fall back to the JS implementation if the native one fails", 5 | "main": "index.js", 6 | "dependencies": { 7 | "tap": "^15.1.6" 8 | }, 9 | "scripts": { 10 | "test": "tap test/*.js --cov" 11 | }, 12 | "repository": { 13 | "type": "git", 14 | "url": "git+https://github.com/isaacs/fs.realpath.git" 15 | }, 16 | "keywords": [ 17 | "realpath", 18 | "fs", 19 | "polyfill" 20 | ], 21 | "author": "Isaac Z. Schlueter (http://blog.izs.me/)", 22 | "license": "ISC", 23 | "files": [ 24 | "old.js", 25 | "index.js" 26 | ], 27 | "tap": { 28 | "statements": 80, 29 | "branches": 30, 30 | "functions": 100, 31 | "lines": 80 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /test/monkeypatching.js: -------------------------------------------------------------------------------- 1 | var t = require('tap') 2 | var rp = require('../') 3 | var fs = require('fs') 4 | 5 | rp.monkeypatch() 6 | t.equal(rp.realpath, fs.realpath) 7 | t.equal(rp.realpathSync, fs.realpathSync) 8 | 9 | rp.unmonkeypatch() 10 | t.not(rp.realpath, fs.realpath) 11 | t.not(rp.realpathSync, fs.realpathSync) 12 | -------------------------------------------------------------------------------- /test/symlinks.js: -------------------------------------------------------------------------------- 1 | var fs = require('fs') 2 | var t = require('tap') 3 | var path = require('path') 4 | 5 | var rp = require('../') 6 | 7 | var lengths = [ 1, 128, 256 ] 8 | 9 | var root = __dirname + '/rptest' 10 | 11 | function clean () { 12 | try { fs.unlinkSync(root + '/a/b') } catch (e) {} 13 | try { fs.rmdirSync(root + '/a') } catch (e) {} 14 | try { fs.rmdirSync(root) } catch (e) {} 15 | } 16 | 17 | t.test('setup', function (t) { 18 | clean() 19 | 20 | fs.mkdirSync(root) 21 | fs.mkdirSync(root + '/a') 22 | fs.symlinkSync('..', root + '/a/b') 23 | 24 | t.end() 25 | }) 26 | 27 | var expect = path.resolve(__dirname + '/rptest/a') 28 | 29 | lengths.forEach(function (len) { 30 | t.test('symlinks = ' + len, function (t) { 31 | var long = root + '/' + Array(len).join('a/b/') + 'a' 32 | 33 | t.plan(2) 34 | t.equal(rp.realpathSync(long), expect) 35 | rp.realpath(long, function (er, actual) { 36 | if (er) { 37 | throw er 38 | } 39 | t.equal(actual, expect) 40 | }) 41 | }) 42 | }) 43 | 44 | t.test('cleanup', function (t) { 45 | clean() 46 | t.end() 47 | }) 48 | --------------------------------------------------------------------------------