Skip to content

feature: Local Modules #146

Closed
Closed
@srcspider

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.

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions