Description
Currently one of the biggest pain points with node development is the problem of referring to local modules (ie. how to avoid the unreadable require('../../../../../something')
).
The more modules you create in your project the biggest the issue becomes.
Node conventions recommends splitting things into modules and uploading to npm however that has some issues of it's own:
- it assumes your code is generic enough to be a npm module
- it doesn't allow you to maintain a library of modules, you're always maintaining a single module; works great for public modules, not that great if it's something private--makes more sense to split into libraries before splitting everything into individual functions (maintaining 100 functions as 100 modules vs just 2-3 libraries)
- it requires you use npm to pull the code in or a private solution that involves npm; if you're pulling private modules you should have the choice of avoiding anything but your own system
There's a simple solution for both, and that's to create "fake" node_modules directories. Something like this:
ProjectRoot/
node_modules/ # your npm modules
src/
node_modules/ # place all your project modules here
yourModule1/ # example module
yourModule2/ # example module
yourModule3/ # example module
main.js
entrypoint.js
This works great since any code inside src/node_modules
can call internal modules and node will resolve them correctly once it's search algorithm goes into src/
.
It also doesn't require any path aliasing (which I believe is frown upon?), so you can just stick everything in src/
into a module, point to src/node_modules/main.js
as the entry point and your entire application is now reusable (either by the world or members of your private group).
The problem with that solution is that it's very annoyingly:
- name biased; even if you use only camel case in your code for presumably good reasons you'll still have these random "node_modules" folders just being an eyesore
- "node_modules" is literally "vendor"
- causes confusion between npm and non-npm modules
- is really annoying to have multiples of, eg. project_modules, shared_modules1, shared_modules2, etc, because you can't have them in the same directory, you have to have each in a different directory level
Solution
Changes to module search algorithm
- if
node_modules
directory is not found in directory, node should search for a.iojs
file - if a
.iojs
file is found then the configuration is read and rules in it apply
Example .iojs
files that resolves our problem
In each custom module library you place the following, unless it's literally called "node_modules"
module.exports = {
// tell node that current directory is root for modules
modules_root: true
};
In the root you place .iojs
file if you wish to expose modules inside them directly to other modules:
modules.exports = {
// directories specified are resolved in order
node_modules_dirs: [ 'sharedLibrary1', 'sharedLibrary2' ]
}
We can now have something like:
ProjectRoot/
node_modules/ # your npm modules
src/
sharedLibrary1/ # private accessible modules
sharedLibrary2/ # private accessible modules
sharedPrivate1/ # private modules, non accessible outside themselves
sharedPrivate2/ # private modules, non accessible outside themselves
someAppModules/ # place all your project modules here
yourModule1/ # example module
yourModule2/ # example module
yourModule3/ # example module
main.js
entrypoint.js
All three directories in src/
would have a copy of the "modules_root" .iojs
file. And in src
you would have a copy of the "node_modules_dirs" .iojs
.
Let's say for example both sharedLibrary1
, sharedLibrary2
, sharedPrivate1
and sharedPrivate2
all have a file lib/merge.js
.
Because the .iojs
applies while still inside them calling require('lib/merge')
would resolve to sharedPrivate1/lib/merge
in one and sharedPrivate2/lib/merge
in the other; which is exactly what we want.
If you call require('lib/merge')
in someAppModules
will resolve to sharedLibrary1/lib/merge
because it's the first one specified in the src/.iojs
file, however if you call it in sharedLibrary2
it will resolve to sharedLibrary2/lib/merge
since it will get resolved by src/sharedLibrary2/.iojs
before it gets resolved by src/.iojs
Backwards compatible convection of someAppModules
to a single application NPM module
npm_module_name/
node_modules/ # your npm dependencies (created by package.json resolution)
src/
node_modules/ # you rename someAppModules to node_modules
yourModule1/ # example module
yourModule2/ # example module
yourModule3/ # example module
main.js
index.js # module.exports = require('./src/node_modules/main.js')
package.json
Of course this assumes you convert some parts of your code to rely on generic modules as opposed to local ones. Easy peasy since everything has a nice grep-able name.