Generate RequireJS "bundles" config dynamically with Grunt - javascript

According to the RequireJS API, the bundles configuration property is used to map modules to their bundle files (in production mode, assuming you have multiple bundles on large-scale projects).
I use Grunt for bundling with r.js of course, how can I generate the bundle configuration dynamically, according to the actual bundling? e.g. if module angular was bundled to common.js, then it should be added to the list of modules of the common bundle.
This part of requirejs configuration should not be maintained manually, as it will result in many mistakes.
Any suggestions?

Version 2.2.0 of RequireJS introduced a new optimizer option to write out a bundles option that is generated from the build:
//Introduced in 2.2.0. Path to file to write out bundles config
//(http://requirejs.org/docs/api.html#config-bundles) found in the module
//layers built by the optimizer. The path is relative to the "dir" config's
//path. Only applies to full project optimization:
//http://requirejs.org/docs/optimization.html#wholeproject
//Only use if the optimized layers are grouped more intricately then just
//a simple optimization of main app entry points. The file path specified
//should be to one that has the top level requirejs.config() call that sets
//up the loader. If using "mainConfigFile", then this path likely should be
//the path to that file where it is placed in the "dir" output directory.
bundlesConfigOutFile: 'some/path/to/main.js',
If the option above does not work for you, then you'll have to do what I did and roll your own code to generate a proper value for bundles.

Related

Webpack watch() with multiple entry points - emitting bundles for non-changed files?

In my webpack.config.js I have 3 separate entry points, one for the JS bundle, one for the main SCSS bundle, and one for a separate SCSS bundle that has no relationship with the main SCSS bundle.
When I use the webpack.watch() API, for some reason editing, say, the JS source files, causes not only the JS bundle to be recompiled, but also the 2 SCSS bundles.
Why is this, and how can I stop this behaviour and ensure that only the entry point that is edited is recompiled?
The reason this is an issue is I'm using browsersync, and for CSS bundle recompiles I'm just injecting the CSS instead of reloading, but on HTML/JS edits its reloading. However if I edit the SCSS and it also recompiles the JS/HTML browsersync triggers a reload instead of a CSS inject.
In that case you need a file that let webpack detect how many changes have be done on each different entry point.
For that, you can use the manifest file provided by commonsChunkPlugin:
For example, if you have the following entry points:
entry: {
app: 'main.js', // main entry point
vendor: ['jquery', 'react'] //Third libraries
}
You can use the plugin CommonsChunkPlugin:
new wepack.optimize.CommonsChunkPlugin({
name: ['vendor', 'manifest']
})
This configuration generates a manifest file as another output. In that case, if you make a change in your 'app' entry point, webpack are only going to recompile the main.js output bundle (according the 'filename' format in the 'output' configuration) because the vendor bundle is already the same.
You can try that with your specific entry points.

Make pdf.js 1.5 and require.js play nice together

In my project I have long used require.js together with the pdf.js library. Pdf.js have until recently been putting itself on the global object. I could still use it in my requirejs config by using a shim. The pdfjs library will in turn load another library called pdf.worker. In order to find this module the solution was to add a property to the global PDFJS object called workerSrc and point to the file on disk. This could be done before or after loading the pdfjs library.
The pdfjs library uses the pdf.worker to start a WebWorker and to do so it needs the path to a source file.
When I tried to update the pdfjs library in my project to a new version (1.5.314) the way to load and include the library have changed to use UMD modules and now everything get's a bit tricky.
The pdfjs library checks if the environment is using requirejs and so it defines itself as a module named "pdfjs-dist/build/pdf". When this module loads it checks for a module named "pdfjs-dist/build/pdf.worker". Since I have another folder structure I have added them to my requirejs config object with a new path:
paths: {
"pdfjs-dist/build/pdf": "vendor/pdfjs/build/pdf",
"pdfjs-dist/build/pdf.worker": "vendor/pdfjs/build/pdf.worker"
}
This is to make the module loader to find the modules at all. In development this works great. When I try to use the requirejs optimizer in my grunt build step however, it will put all of my project files into one single file. This step will try to include the pdf.worker module as well and this generates an error:
Error: Cannot uglify2 file: vendor/pdfjs/build/pdf.worker.js. Skipping
it. Error is: RangeError: Maximum call stack size exceeded
Since the worker source needs to be in a single file on disk I don't want this module to be included.
So I've tried two different config-settings in the requirejs config.
The first attempt was to override the paths property in my grunt build options:
paths: {
"pdfjs-dist/build/pdf.worker": "empty:"
}
The second thing to test is to exclude it from my module:
modules: [{
name: "core/app",
exclude: [
"pdfjs-dist/build/pdf.worker"
]
}]
Both techniques should tell the optimizer not to include the module but both attempts ended up with the same error as before. The requirejs optimizer still tries to include the module into the build and the attempt to uglify it ends up with a RangeError.
One could argue that since the uglify step fails it will not be included and I can go about my bussiness, but if the uglify step should happen to start working at a new update of pdfjs - what then?
Can anyone help me figure out why the requirejs config won't just exclude it in the build step and how to make it do so.
I found out what the core of my problem was and now I have a way to solve the problem and make my build process to work. My build step in grunt is using grunt-contrib-requirejs and I needed to override some options in the config for this job.
I didn't want the pdf.worker module to be included in my concatenated and minified production code.
I didn't want r.js to minify it only to later exclude it from the concatenated file.
I tried to solve the first problem thinking that it would mean that the second problem also should be solved. When I figured out the two were separate I finally found a solution.
In the r.js example on github there is a property named fileExclusionRegExp. This is what I now use to tell r.js not to copy the file over to the build folder.
fileExclusionRegExp: /pdf.worker.js/
Second, I need to tell the optimizer to not include this module in the concatenated file. This is done by overriding the paths property for this module to the value of "empty:".
paths: {
"pdfjs-dist/build/pdf.worker": "empty:"
}
Now my grunt build step will work without errors and all is well.
Thanks to async5 for informing me about the bug with uglify and the pdf.worker. The workaround is applied in another grunt task that uglify the worker and copies it into the build-folder separately. The options object for the grunt-contrib-uglify task will need this property in order to not break the pdf.worker file:
compress: {
sequences: false
}
Now my project works great when built for production.

Loading specific module from concatenated file using RequireJS

Trying to use the URI.js library in a project, but having trouble with RequireJS. URI's readme indicates that it works with requirejs - which is true when you're using the source - but not when using the minified/concatenated distribution file on its own - as you would in production.
Their build process scoops several libraries into a single .min file, and they all define themselves as modules.
No matter how I require that script, the only argument I receive is the first module in their distribution file (IPv6) which is not what I need.
Is there something trivial I'm missing?
<script>
require.config({
paths: {
urijs: 'dist/URI'
}
});
require(['urijs'], function(URI) {
console.log(URI);
});
</script>
Indeed the minified file cannot be used because it is not correctly built. All the define calls in it are anonymous whereas they should include the module name. If they had, you'd be able to use the minified file.
To get it to work, in the paths configuration, I've put urijs to point to the directory that contains all the individual .js files of the modules, and then I require URI.js as 'urijs/URI'. I let r.js minify it for distribution of my code.
I've installed it with npm but I've just checked with bower and the process should be exactly the same.

How to use gulp to build JavaScript bundles?

I want to use gulp to build bundles of JavaScript files.
For example I have the following structure in my project:
/vendor/vendor1/vendor1.js
/vendor/vendor2/vendor2.js
/js/includes/include1.js
/js/includes/include2.js
/js/bundle1.js
/js/bundle2.js
There are vendor includes (1-2), local includes (3-4), and bundle files (5-6).
Vendor includes are just third-party JavaScript libraries installed with bower or composer. They can be CommonJS, AMD or just a plain-old jQuery plugins.
I want to specify dependencies inside of a bundle files like this:
/js/bundle1.js
(function() {
// Vendor includes.
include('vendor1');
include('vendor2');
// Local includes.
include('includes/include1.js');
include('includes/include2.js');
// Some code here.
})();
I want gulp to process this source file and create a final distribution file (bundle) ensuring that all dependencies (includes) are merged together in a single file. So I can include foo.js from my HTML and all dependencies will be available to it.
I want to have a clear and robust system to manage all dependencies inside of a project and build distribution files.
How can I achieve this?
What format should I use for my own scripts (AMD, CommonJS, other)?
How do I specify dependencies in my source bundle files?
How do I build distribution?
Your question is posed as if there's a single answer, but there isn't. The problem you're trying to solve is one that many people have solved in many different ways, and you've identified two of the major options: AMD and CommonJS. There are other ways, but given that you might be new to Javascript dependency management as well as gulp, I'd recommend going with something that's relatively straightforward (even though this subject is inherently not straightforward).
I think the easiest route for you might be:
use CommonJS to express the dependencies
use browserify to resolve them into bundles
in browserify, use the "UMD" method so that you get a single bundle that will work for apps that use either AMD or CommonJS or are not using either of these dependency management systems
The statement in gulp to run browserify as such might look something like:
var browserify = require('gulp-browserify');
gulp.src('bundles/bundle1.js', {read: false})
.pipe(browserify({
'standalone': true
})
.pipe(rename('bundle1Output.js'))
.pipe(gulp.dest('dist'));
That should give you a dist/bundle1Output.js file.
There is a gulp plugin for this:
https://www.npmjs.com/package/gulp-include
It should do what you want, except that in your bundle file instead of this:
(function() {
// Vendor includes.
include('vendor1');
include('vendor2');
// Local includes.
include('includes/include1.js');
include('includes/include2.js');
// Some code here.
})();
You would have to write:
//=require vendor1/**/*.js
//=require vendor2/**/*.js
//=require includes/include1.js
//=require includes/include2.js
// Some code here

require.js optimization: deep dependencies

I'm trying to optimize (i.e. build) my app which is dependent from ember-data. So as the path for ember-data I set this file https://github.com/emberjs/data/blob/master/packages/ember-data/lib/main.js but r.js includes only this particular file and omit it's dependencies. I thought maybe it's because of some kind of paths issue but I can't see any error messages in console.
Make sure that in your build file (the one you pass to r.js as a build config), you have:
findNestedDependencies: true
R.js is omitting so called "inline dependencies" by default so if you do have in your main.js i.e.
require(["sth"],function(){
require(["sthelse"]);
});
R.js will only load "sth" by assuming that "sthelse" should be loaded during runtime.
; )

Categories