This is tad long but I'll need the code example to illustrate my confusion. After which I am interested to the answer for the following:
require('module')
instead of require('../../src/module')
or require('./module')
?./index.js
in spec/specs.js
without duplicating work? (And preventing src/app.js
from running as it's an entry module).I've started several browser based projects already and love browserify and grunt. But each project dies at the same point in my development/learning curve. Once I add testing to the mix and have to manage two browserify bundles (app.js
and spec/specs.js
) the whole system falls apart. I'll explain:
I use grunt-browserify and set my initial directory:
.
├── Gruntfile.js
├── index.js (generated via grunt-browserify) [1]
├── lib
│ ├── jquery
│ │ └── jquery.js [2]
│ └── jquery-ui
│ └── jquery-ui.js [3]
├── spec
│ ├── specs.js (generated via grunt-browserify) [4]
│ └── src
│ ├── spec_helper.js (entry)
│ └── module_spec.js (entry)
└── src
├── app.js (entry)
└── module.js
src/app.js
) and does a code walk to bundle all required modules.jquery
.jquery-ui
without a shim (required after you var $ = require('jquery')
).spec/src
as entry modules.I'll step through my config:
browserify: {
dist: {
files: {
'index.js': ['src/app.js']
}
}
}
// in app.js
var MyModule = require('./module'); // <-- relative path required?!
Happy
Now add jquery:
browserify: {
options: {
shim: {
jquery: {
path: 'lib/jquery/jquery.js',
exports: '$'
}
},
noParse: ['lib/**/*.js'],
alias: [
'lib/jquery-ui/jquery-ui.js:jquery-ui'
]
},
dist: {
files: {
'index.js': ['src/app.js']
}
}
}
// in app.js
var $ = require('jquery');
require('jquery-ui');
var MyModule = require('./module');
Happy
Now add specs:
options: {
shim: {
jquery: {
path: 'lib/jquery/jquery.js',
exports: '$'
}
},
noParse: ['lib/**/*.js'],
alias: [
'lib/jquery-ui/jquery-ui.js:jquery-ui'
]
},
dist: {
files: {
'app.js': 'src/app.js'
}
},
spec: {
files: {
'spec/specs.js': ['spec/src/**/*helper.js', 'spec/src/**/*spec.js']
}
}
// in app.js
var $ = require('jquery');
require('jquery-ui');
var MyModule = require('./module');
// in spec/src/module_spec.js
describe("MyModule", function() {
var MyModule = require('../../src/module'); // <-- This looks like butt!!!
});
Sad
To summarize: How do I...
require('module')
instead of require('../../src/module')
or require('./module')
?./index.js
in spec/specs.js
without duplicating work? (And preventing src/app.js
from running as it's an entry module).Simple answer:
The simplest is to use the paths
option of browserify. I use it for some months with great success. I have even made a starter kit that uses this feature: https://github.com/stample/gulp-browserify-react-phonegap-starter
var b = browserify('./app', {paths: ['./node_modules','./src/js']});
paths - require.paths array to use if nothing is found on the normal node_modules recursive walk
If you have a file in src/js/modulePath/myModule.js
this won't let you write require("myModule")
everywhere, but rather require("modulePath/myModule")
, from any of your other source files.
Deprecated option?
It does not seem so!
The Browserify module resolution algorithm mirrors the resolution algorithm in NodeJS.
The paths
option of Browserify is thus the mirror of NODE_PATH
env variable behavior for NodeJS.
The Browserify author (substack) claims in this SO topic that the NODE_PATH
option is deprecated in NodeJS and thus it is also deprecated in Browserify and could be removed in next versions.
I do not agree with this claim.
See the NODE_PATH documentation. It is not mentionned that the option is deprecated. However there is still an interesting mention that does in the direction of substack's claim:
You are highly encouraged to place your dependencies locally in node_modules folders. They will be loaded faster, and more reliably.
And this question has been posted in 2012 on the mailing list.
Oliver Leics: is NODE_PATH deprecated?
Ben Noordhuis (ex core NodeJS contributor): No. Why do you ask?
And if something is not removed in NodeJS resolution algorithm, I don't think it will be removed anytime soon from Browserify :)
Conclusion
You can either use paths
option or put your code in node_modules
like the official documentation and Browserify author recommends.
Personally, I don't like the idea to put my own code in node_modules
as I simply keep this whole folder out of my source control. I use the paths
option for some months now and did not have any problem at all, and my build speed is pretty good.
The substack's solution of putting a symlink inside node_modules
could be convenient but unfortunately we have developers working with Windows here...
I think there's however a case where you don't want to use the paths
option: when you are developping a library published on an NPM repository that will be required by other apps. You really don't want these library clients to have to setup special build config just because you wanted to avoid relative path hell in your lib.
Another possible option is to use remapify
All the answers here about aliases and opts.paths
/$NODE_PATH
are not great because that approach is a deprecated part of the module system in node and browserify, so it could stop working at any time.
You should learn how the node_modules algorithm works so you can effectively organize your code in a way that plays well with nested node_modules
directories.
There is a section in the browserify handbook that covers avoiding ../../../../../../.. relative path issues. It can be summarized as:
node_modules/
or node_modules/app
so that you can require('yourmodule')
or require('app/yourmodule')
depending on which you prefer.Don't use opts.path
/$NODE_PATH
. It makes your project:
These answers depend on how the rest of your project is setup, but maybe it's a good starting point. Also, you will need to use current v2 beta of grunt-browserify for this to actually work (npm install grunt-browserify@2
).
1.
You can use aliasMapping to create some dynamic aliases for your modules. Just for clarity, lets move all your modules to src/modules/
. Then, aliasMapping configuration could be something like this:
options: {
aliasMappings: {
cwd: 'src',
src: ['modules/**/*.js']
}
}
Lets suppose you have a module in src/modules/magic/stuff.js
, then you can require it like this, regardless of where the .js file that's doing the require is located:
var magicStuff = require('modules/magic/stuff.js');
2.
Not sure about this one. Your project structure shows a spec/index.js
, but you mention spec/specs.js
. Are they supposed to be the same file?
Anyways, what duplicate work are you talking about? Because ./index.js
has a different entry file than spec/index.js
. If you are looking for a way to include ./index.js
in specs/
, then maybe you can copy it before running the tests instead of building it from scratch.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With