The strategy I took with node_modules/, components/ and bower_modules/ was to just follow whatever folder structure it found as far as necessary and look for manifest files (package.json, bower.json and component.json) and register modules at the path of wherever one of those three file types is found. This approach allows any folder organization whatsoever. This information was then saved to a key-value store and the store is only updated when the mtime of the folder changes. This makes startup relatively fast since folders are only re-indexed if something has genuinely changed.
Writing an algorithm that is dependent on the folder structure to me is a fundamentally bad idea most of the time, especially when you have a manifest file that better identifies a module. Additionally, for modules with semver ranges or installed from unusual locations (i.e. URIs: git:// git+ssh://, https://, etc.), you can write an additional metadata file with install information and version locking information (like gemfile.lock)
If people don't rely on the file system organization as an API, npm and node's require() algorithm can trivially reorganize or even use different folder organization schemes on different operating systems if there were a good reason to do so.
Let's say you want to explore the dependency `foo` in your project. At the root of your project type `npm explore foo` and press enter and you'll be in a shell exploring the foo module. Then if you want to return, type `exit` and return and you'll be back in your project. Or if you want to go down the rabbit hole to a dependency of foo called bar, type `npm explore bar` and press enter.
`npm explore` is a great way to explore things and navigate to the dependencies used by the dependency you want to investigate. It basically uses the information in the current package.json to find the dependency you want.