From fa6191f6ac65a8a303a22933e183e1f5a7381976 Mon Sep 17 00:00:00 2001 From: aleclarsoniv Date: Thu, 15 Sep 2016 05:22:07 -0700 Subject: [PATCH] Support symlinks-to-symlinks Summary: In response to [this comment](https://github.com/facebook/react-native/pull/9009#issuecomment-245322397). I could be wrong here, but I think Watchman can't handle symlinks, so we need to make sure symlinks-to-symlinks are handled up front. Closes https://github.com/facebook/react-native/pull/9792 Differential Revision: D3858349 Pulled By: bestander fbshipit-source-id: f3a34dae90ed9a7004a03158288db5e1932bfc69 --- local-cli/server/findSymlinksPaths.js | 54 +++++++++++++++++++-------- local-cli/server/server.js | 6 +-- 2 files changed, 41 insertions(+), 19 deletions(-) diff --git a/local-cli/server/findSymlinksPaths.js b/local-cli/server/findSymlinksPaths.js index af64f2daf..8dc7d081e 100644 --- a/local-cli/server/findSymlinksPaths.js +++ b/local-cli/server/findSymlinksPaths.js @@ -1,28 +1,50 @@ const path = require('path'); const fs = require('fs'); -function isSubPathOfPath(parentPath, subPath) { - return !path.relative(parentPath, subPath).startsWith('..' + path.sep); -} - -function isSubPathOfPaths(parentPaths, subPath) { - return parentPaths.some(parentPath => isSubPathOfPath(parentPath, subPath)); -} - /** - * Find and resolve symlinks in `lookupFolder`, filtering out any - * paths that are subpaths of `existingSearchPaths`. + * Find and resolve symlinks in `lookupFolder`. + * Ignore any descendants of the paths in `ignoredRoots`. */ -module.exports = function findSymlinksPaths(lookupFolder, existingSearchPaths) { +module.exports = function findSymlinksPaths(lookupFolder, ignoredRoots) { const timeStart = Date.now(); const folders = fs.readdirSync(lookupFolder); - const resolvedSymlinks = folders.map(folder => path.resolve(lookupFolder, folder)) - .filter(folderPath => fs.lstatSync(folderPath).isSymbolicLink()) - .map(symlink => path.resolve(process.cwd(), fs.readlinkSync(symlink))) - .filter(symlinkPath => !isSubPathOfPaths(existingSearchPaths, symlinkPath)); - const timeEnd = Date.now(); + const resolvedSymlinks = []; + folders.forEach(folder => { + const visited = []; + + let symlink = path.resolve(lookupFolder, folder); + while (fs.lstatSync(symlink).isSymbolicLink()) { + const index = visited.indexOf(symlink); + if (index !== -1) { + throw Error( + `Infinite symlink recursion detected:\n ` + + visited.slice(index).join(`\n `) + ); + } + + visited.push(symlink); + symlink = path.resolve( + path.dirname(symlink), + fs.readlinkSync(symlink) + ); + } + + if (visited.length && !rootExists(ignoredRoots, symlink)) { + resolvedSymlinks.push(symlink); + } + }); + + const timeEnd = Date.now(); console.log(`Scanning ${folders.length} folders for symlinks in ${lookupFolder} (${timeEnd - timeStart}ms)`); return resolvedSymlinks; }; + +function rootExists(roots, child) { + return roots.some(root => isDescendant(root, child)); +} + +function isDescendant(root, child) { + return root === child || child.startsWith(root + path.sep); +} diff --git a/local-cli/server/server.js b/local-cli/server/server.js index 75dbf7d19..fe0ee73ae 100644 --- a/local-cli/server/server.js +++ b/local-cli/server/server.js @@ -19,9 +19,9 @@ const NODE_MODULES = path.resolve(__dirname, '..', '..', '..'); * Starts the React Native Packager Server. */ function server(argv, config, args) { - args.projectRoots = args.projectRoots.concat( - args.root, - findSymlinksPaths(NODE_MODULES, args.projectRoots) + const roots = args.projectRoots.concat(args.root); + args.projectRoots = roots.concat( + findSymlinksPaths(NODE_MODULES, roots) ); console.log(formatBanner(