How to create a vendor bundle with grunt-browserify - javascript

I'm working on a single page app that requires several third-party libs. To reduce build time I'm trying to create two separate bundles: one for the libs code and one for the app code. My build process uses grunt-browserify to generate the bundles. Here's my browserify task (I'm using grunt-load-tasks to modularize my Grunt tasks):
var libs = [
'backbone',
'backbone-relational',
'backbone.babysitter',
'backbone.wreqr',
'bootstrap',
'bootstrap-growl',
'handlebars',
'jquery',
'marionette',
'underscore'
];
module.exports = {
options: {
external: libs
},
libs: {
src: [],
dest: './build/js/libs.js',
options: {
external: null,
require: libs
}
},
app: {
src: ['./frontend/js/app.coffee'],
dest: './build/js/app.js',
options: {
browserifyOptions: {
debug: true,
extensions: ['.coffee'],
},
watch: true
}
}
}
This successfully creates the two separate bundles and my app is functional after including the bundles on the page. However I'm noticing that Backbone and Handlebars are being included in both the libs.js bundle and the app.js bundle when I would have expected them to be included only in the libs.js bundle. Any idea what I'm doing wrong?

Related

RequireJS bundles fail in optimizer - "modules share the same URL"

tl;dr
RequireJS optimizer doesn't like me defining bundle definitions on a module, but also does not find the modules if I don't define the bundles.
long version
I am getting the following error when trying to use the requirejs optimizer:
Error: Module loading did not complete for: scripts/simulation.bundle, app_mini, testservice
The following modules share the same URL. This could be a misconfiguration if that URL only has one anonymous module in it:
.../web/dist/scripts/app.bundle.js: app_mini, testservice
I am actually using grunt-contrib-requirejs to optimize my js scripts for production. It was all working fine before adding the simulator.bundle
I have 3 bundles:
app.bundle (main bundle)
simulation.bundle
vendor.bundle
This is the modules option of the requirejs grunt tasks
[{
name: 'scripts/vendor.bundle',
exclude: [],
override: {
paths: {
angular: 'bower/angular/angular',
jquery: 'bower/jquery/dist/jquery',
ngRoute: "bower/angular-route/angular-route"
},
shim: {
angular: {
exports: 'angular',
deps: ['jquery'] // make jquery dependency - angular will replace jquery lite with full jquery
},
bundles: {
'scripts/app.bundle': ['app_mini', 'testservice'],
},
}
}
},
{
name: 'scripts/simulation.bundle',
exclude: [],
override: {
paths: {},
shim: {},
bundles: {
'scripts/vendor.bundle': ['angular', 'jquery'],
'scripts/app.bundle': ['app_mini', 'testservice']
}
}
},
{
name: 'scripts/app.bundle',
exclude: ['scripts/vendor.bundle'],
override: {
paths: {
app_mini: 'scripts/app.mini',
testservice: 'scripts/features/test.service'
},
shim: {},
bundles: {
'scripts/vendor.bundle': ['angular', 'jquery']
}
}
}
]
The bundles in simulation.bundle seem to be the problem. However, if I remove them, the files cannot be found:
>> Error: ENOENT: no such file or directory, open
>> '...\web\dist\app_mini.js'
>> In module tree:
>> scripts/simulation.bundle
The simulation.bundle is just a dummy module, loading angular and app_mini:
define(['app_mini', 'angular'], function(app_mini, angular) {
// nothing here
}
So either way, the optimizer cannot process the dependencies. How do I have to configure it to make it work?
Alright, once again I am posting the answer to my own question, and I hope some other people will benefit from my mistakes ;)
So what I found out is:
Bundle config is only for requireJS and not for the optimizer!
The bundles I defined in the config are leading to the error of modules sharing the same url.
The right way to do it, is to define ALL the paths for ALL the modules, and to specifically exclude the modules by name that should not be included in a module.
For example, app_mini should go into the app.bundle, but because it is required in the simulation.bundle it would get included there, because excluding app.bundle is not yet possible (it has not been optimized at this point), we need to exclude app_mini directly.
So a working config would look like this: (not tested)
paths: {
angular: 'bower/angular/angular',
jquery: 'bower/jquery/dist/jquery',
ngRoute: "bower/angular-route/angular-route"
app_mini: 'scripts/app.mini',
testservice: 'scripts/features/test.service'
},
shim: {
angular: {
exports: 'angular',
deps: ['jquery'] // make jquery dependency - angular will replace jquery lite with full jquery
}
},
modules: [
{
name: 'scripts/vendor.bundle',
exclude: [],
},
{
name: 'scripts/simulation.bundle',
exclude: [`app_mini`],
},
{
name: 'scripts/app.bundle',
exclude: ['scripts/vendor.bundle'],
}
}]

Webpack exclude entries for CommonsChunkPlugin

I am trying to set up webpack production configuration. All looks well. However, I realized that while using the commons chunk plugin, it covers all the files in common as expected. What I want to do is, separation of common library modules and common application modules. My config file is :
module.exports = {
entry: {
lib: ["react", "react-dom"],
app: "./ui-v2/app/app.js",
app2: "./ui-v2/app/app2.js"
},
output: {
path: path.join(__dirname, "target/ui/v2"),
filename: "/app/[name].[chunkhash].min.js"
},
module: {
loaders: [
{
test: /\.js$/,
exclude: /node_modules/,
loader: "babel-loader"
},
{
test: /\.(png|jpg|svg)/,
loader: "file-loader?name=img/[name].[hash].[ext]"
// loaders: ["url", "image-webpack"]
},
{
test: /\.scss$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader!autoprefixer-loader!sass-loader", {
publicPath: __dirname
})
},
{
test: /\.(woff|woff2|ttf|eot)$/,
loader: "file-loader?name=fonts/[name].[hash].[ext]"
}
]
},
plugins: [
clean,
new webpack.optimize.CommonsChunkPlugin("common", "app/common.[chunkhash].js"),
new webpack.ProvidePlugin({
React: "react",
ReactDOM: "react-dom",
$: "jquery",
_: "lodash"
}),
new webpack.optimize.UglifyJsPlugin({
compress: {
warnings: false
sourceMap: true
},
mangle: {
except: ["exports", "import", "$", "_", "require", "React", "ReactDOM"]
}
}),
new ExtractTextPlugin("styles/[name].[contenthash].css"),
new Manifest()
]
}
Basically I have 3 modules in the app; app.js, app2.js and a common component user.js.
What I want to achieve is to bundle all library related files like react, react-dom, lodash, etc in a lib bundle, and common application components like user.js in a common bundle. In order to do this, I thought there might be an option to exclude the files that I don't want them to go to "common" file. If I use this output, what is the point for long term caching files for library bundles because whenever I get a common component in my project, they will go into the common bundle and the content hash will be different, but nothing changes in this library files like react, jquery, lodash, etc.
Anyway, what I have at the end of build process is everything still goes into the common bundle and lib has nothing and the file sizes are :
app.<hash>.min.js -> 3.05KB
app2.<hash>.min.js -> 3.05KB
lib.<hash>.min.js -> 165 Bytes (has almost nothing!)
common.<hash>.js -> 678 KB
Is there any way to achieve what I want or what would be the best approach to a production build in similar cases? Thank you!
Its because the first parameter for CommonsChunkPlugin is "common" where it should be "lib". The plugin picks up the entry with a name matching with the value of its first parameter.
A simple example config picked from webpack's wiki -
var webpack = require("webpack");
module.exports = {
entry: {
app: "./app.js",
vendor: ["jquery", "underscore", ...],
},
output: {
filename: "bundle.js"
},
plugins: [
new webpack.optimize.CommonsChunkPlugin(/* chunkName= */"vendor", /* filename= */"vendor.bundle.js")
]
};
Note that the "vendor" entry is again specified in CommonsChunkPlugin
You should check out Webpack's DLL Plugin.
https://github.com/webpack/webpack/blob/cb3d8e2097503c7245c5dda5b7a6e9d63421a72b/examples/dll/README.md
With this plugin you bundle up common 3rd party vendor dependencies such as React and friends in a DLL, which is essentially just a JSON Manifest that goes along with your requires wrapped in webpack context and cached to disk.
In your project code, you would have your shared components which depend on React and friends, and you would have your application code which depend on your shared components as well as react and friends.
Your project would incorporate the DLL Reference plugin as you can see here:
https://github.com/webpack/webpack/blob/cb3d8e2097503c7245c5dda5b7a6e9d63421a72b/examples/dll-user/README.md
This will see to it that your shared components and your application code pull React and other 3rd party modules from the same DLL bundle. This can help improve build times and the performance of the dev server and hot module reloading.

Webpack not loading partial files from the same root

I am compiling a javascript bundle with Webpack. I am specifiying a single file in my build task:
webpack({
entry: 'src/js/main.js',
output: { path: 'dist/js' }
}, function(e) {});
And then I am specifying some async packages in my bundle
require([ 'test.json' ], function() {});
Both the main bundle and the partial bundles compile into "dist/js/main.js", "dist/js/1.main.js", etc.
The main bundle loads from "localhost:8000/dist/js/main.js," but the async/partial dependencies are trying to load from "localhost:8000/1.main.js". Am I missing a critical piece of this?
You need to set the publicPath property, eg:
{
entry: 'src/js/main.js',
output: {
path: 'dist/js',
publicPath: '/dist/js/'
}
}
References:
output.publicPath

Browserify: External Hash ID Keeps Changing

I'm using Grunt-Browserify to load a library (jQuery) in one bundle and reference that library as external in other bundles.
Browserify assigns a unique hash id to the external library, and everything works fine for a single developer.
However, when a second developer runs the same Grunt task, the unique id for jQuery changes -- breaking any bundles that are still looking for it at the old "address".
Does anyone know how to control the id assigned to an external library in Browserify -- or how to prevent Browserify from using a hash id for external dependencies?
Here's my current configuration:
browserify: {
main: {
files: {
'./dist/main.js': ['./dev/js/main.js']
},
options: {
require: ['jquery'],
fullPaths: true,
watch: true
}
},
bundles: {
files: {
'./dist/bundle-1.js': ['./dev/bundle-1.js'],
// ...
},
options: {
external: ['jquery'],
fullPaths: true,
watch: true
}
}
}

Require JS Optimizer with Multiple Modules: How to not mirror entire javascript directory

I currently have four different modules set up, which all build relative to the path where my initialize file exists. The initialize file basically includes the main configuration file and then a file that sets up all of the dependencies for the single page app and initializes it. This all works fine, the only thing I don't understand is why the require js optimizer mirrors the entire js directory. I would like to just have the four module scripts in my script-build folder where all the minimized scripts exists. Is there a way to do this?
Thanks in advance.
EDIT** Here is the config file in case this helps
({
baseUrl: '../',
preserveLicenseComments: "true",
dir: '../../Scripts',
findNestedDependencies: "true",
mainConfigFile: '../main.js',
modules: [
{
name: 'main',
include: ['main', 'requireLib']
},
{
name: "apps/booking",
include: ["apps/booking"],
exclude: ["main"]
},
{
name: 'apps/tour',
include: ['apps/tour'],
exclude: ["main"]
},
{
name: 'apps/search',
include: ['apps/search'],
exclude: ["main"]
}
],
skipDirOptimize: true,
paths: {
requireLib: 'libs/require/require'
},
include: 'requireLib'
})
You can combine multiple excludes in one regex pattern with the | operator. For example:
fileExclusionRegExp: /^(r|build)\.js$/
This excludes r.js & build.js files. Similarly you can achieve for your directory or other files.

Categories

Resources