Uglify or concat a JavaScript file conditionallly - node.js

I have in my Scripts folder a "Lib" folder and an "App" folder.
The Lib folder contains 3rd part library JavaScript files. Some of these are minified without the original sources, others we have the original sources for.
The App folder contains all of our own JavaScript files, all of which are not minified.
I'm new to Grunt but I have a gruntfile which does the following:
Uglifies all the JS files in the Lib folder and produces minified versions with sourcemaps.
Uglifies all the JS files in the App folder and produces minified versions with sourcemaps.
Obvious problem: some of the files in the Lib folder are minified, so minifying them again/generating source maps is a bad idea and can fail for various reasons.
My solution: I run Uglify only on .js files in the Lib folder into lib-unmin.min.js. I then concat all the already minified files into a lib-min.min.js file, then I concat both those files together to get lib.min.js.
The new problem
What if I can't concat the already minified scripts to the end of the other minififed scripts without it breaking?
I have a dependency issue like this:
scripts/lib/a.js (required for b to run)
scripts/lib/b.min.js (required for c to run)
scripts/lib/c.js (required for the App scripts to run)
If I have an array of these file paths in my gruntfile, in that order, what's the easiest way of uglifying/concating all the files into a single minified JS file in that order, making sure we don't attempt to minify the minified file?
What do other developers do in similar situations?
Thanks!

I like to concat the files then uglify all of them together. This way uglify makes sure there aren't duplicate variable values overriding each other when it compresses the variable names.
You can bundle as many files as you want in the concats. Make different concat groups to uglify together and maintain the 'first, second, third, ...' order like this:
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
app: {
options: {
sourceMap: true,
sourceMapName: 'build/maps/map.map'
},
files: {
'build/app.min.js': ['build/js/app-first-unmin.js', 'build/js/app-second-min.js', 'build/js/app-third-unmin.js']
}
}
},
concat: {
options: {
separator: ';'
},
firstUnminified: {
src: [
'lib/underscore.js'
],
dest: 'build/js/app-first-unmin.js'
},
secondMinified: {
src: [
'lib/moment.min.js'
],
dest: 'build/js/app-second-min.js'
},
thirdUnminified: {
src: [
'lib/favico.js'
],
dest: 'build/js/app-third-unmin.js'
}
},
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['concat:firstUnminified', 'concat:secondMinified', 'concat:thirdUnminified','uglify']);
};

Related

karma config moving a file to base directory

Is there a way to move a file loaded in the Karma config to new directory, such as /base/? I have an external project which contains the source files. I am able to load the file via ../../directory. However, it inserts the file into the /absolute/ folder. If I load the test(s) on another computer, the directory of /absolute/ is different. Is there a preprocessor or some configuration I am missing? I searched through the karma documentation, but couldn't find what I am looking for.
// list of files / patterns to load in the browser
files: [
{ pattern: 'node_modules/jquery/dist/jquery.min.js', included: false },
{ pattern: 'node_modules/knockout/build/output/knockout-latest.js', included: false },
{ pattern: 'node_modules/knockout-mapping/dist/knockout.mapping.min.js', included: false },
{ pattern: 'node_modules/knockout-postbox/build/knockout-postbox.min.js', included: false },
{ pattern: '../FantasyFootball/wwwroot/js/site.js', included: false, served: true, watched: true },
{ pattern: 'tests/*.js', included: false },
{ pattern: 'test-main.js', included: true }
],
Karma generates paths that start with /absolute/... for files you ask it to
load files that are outside Karma's basePath. Karma does this because there's no
simple way to provide a path to these files through the usual /base/ path that
is both unique and inferable. If you are to provide a relative path to a
file above basePath, Karma cannot just combine it with /base/. For instance,
if you had ../my/file.js and ../../my/file.js in your files array, then
combined with /base/, they would both turn into the path /my/file.js. For
cases like this, Karma generates a unique and inferable path by using
/absolute/ and appending the absolute path of the file on disk.
Ok, so where does that leave you? You want Karma to load files outside
basePath and you want to be able to use their paths in your loader's setup.
The problem can be overcome. You can modify karma.conf.js to provide
information useful to compute the paths you need:
client: {
absoluteTopDir: path.join("/absolute/", __dirname),
},
Then in test-main.js, you need to use the absoluteTopDir parameter:
var absoluteTopDir = window.__karma__.config.absoluteTopDir;
require.config({
baseUrl: "...",
paths: {
foo: absoluteTopDir + "../../my/file",
},
});
I've run Karma for years without any trouble but I recently ran into the problem
you ran into and used the method I described above in one of my test suites with
great success.
I've used this solution both with RequireJS and SystemJS.

Can I set one config for single and multiple optimization

file structure
assets/js/
- build/
- plugin/
jquery.min.js
- src/
index.js
config.js
builds.js
require.js
assets/js/src/index.js
requirejs(['jquery']);
assets/js/config.js
requirejs.config({
baseUrl: './',
paths: {
jquery: 'plugin/jquery.min'
}
})
If I want to use r.js to optimize the file, just execute r.js -o config.js name=src/index out=build/index.js, the r.js will compile a file into build/index.js with optimization and dependency, but there will be many files need to compile in the future, so I create a builds.js
assets/js/builds.js
({
appDir: 'src',
dir: 'build',
mainConfigFile: 'config.js',
modules: [
{name: 'index'}
]
})
If I run r.js -o builds.js, I will got wrong path message.
Error: Error: ENOENT: no such file or directory, open 'D:\www\r\build\plugin\jquery.min.js'
I need to go back to config.js, and edit the path relative to src.
requirejs.config({
baseUrl: './',
paths: {
jquery: '../plugin/jquery.min'
}
})
It will work, but is it possible to write one config file for both purpose?
Specify paths again in the build file, relative to /src.
builds.js
({
appDir: 'src',
baseUrl: './',
dir: 'build',
modules: [
{name: 'index'}
],
paths: {
jquery: '../plugin/jquery.min'
}
})
The paths in the build file work differently than in the config file.
The appDir option of /src specifies that all your scripts are located in /src, however your config.js and folder structure have the /plugins outside of /src.
When the jquery path is resolved, the paths in the config.js file are used because the mainConfigFile option is used.
All module paths would need to be redefined in your build file in addition to your config file. This is because all module paths are resolved relative to the baseUrl, which is in relation to the appDir in the build file – this is the confusing bit.
Reference the following r.js example build file. https://github.com/jrburke/r.js/blob/master/build/example.build.js
The official doc on the RequireJS optimizer contains a helpful section about path resolution.
http://requirejs.org/docs/optimization.html#basics
Relative path resolution rules:
In general, if it is a path, it is relative to the build.js file used to hold the build options, or if just using command line arguments, relative to the current working directory. Example of properties that are file paths: appDir, dir, mainConfigFile, out, wrap.startFile, wrap.endFile.
For baseUrl, it is relative to appDir. If no appDir, then baseUrl is relative to the build.js file, or if just using command line arguments, the current working directory.
For paths and packages, they are relative to baseUrl, just as they are for require.js.

Grunt Watch LESS files in folder and create css when less file is changed

I am looking for GRUNT automation task which watches the less files in the root directory and gives us the css files of the same name as the less files in the same directory like
Root Folder
package.json
Gruntfile.js
Folder1
file1.less
file2.less
Folder2
file3.less
file4.less
My Gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
//our LESS options
less: {
development: {
files: {
"": "" //not able to write this line, how can i mention which file to compile as i am watching the entire folder
}
}
},
watch: {
css: {
files: '**/*.less',
tasks: ['less']
}
}
});
//load our tasks
grunt.loadNpmTasks('grunt-contrib-less');
grunt.loadNpmTasks('grunt-contrib-watch');
//default tasks to run
grunt.registerTask('default', ['less']);
}
Not able to mention the source less file name and result css file name in less task config, as i am watching the entire folder, i want to create the css file for the corresponding less file in the same path as less, whenever particular css is changed. Want to compile the less file which is changed , not all the less files to be compiled, if one less is changed.
Thanks in advance for any help.
You can use grunt-newer to configure you less task to run with newer files only.
npm install grunt-newer --save-dev
once the plugin is installed, add
grunt.loadNpmTasks('grunt-newer');
edit the watch task:
watch: {
css: {
files: '**/*.less',
tasks: ['newer:less']
}
}
Have a look at the grunt.js docu and see how to build files object dynamically.

Understanding the mean stack and integrating uglify.js and stylus

I'm just getting started with the MEAN stack (https://github.com/linnovate/mean), so I'm pretty sure my question is going to look very basic to an expert, so my apologies in advance!
While I think it would be a gread addition to what this stack already has to offer, I cannot manage to integrate Uglify.js and stylus
Also someone already asked this, but it would make sense to me to use Jade template for both server and public views, at least for a matter of standardization.
I have tried playing with the grunt file and server.js, renaming some files, but all I managed to achieve so far, is break the original project...
Thanks in advance!
EDIT: Just found a fork of this project which has just added support for jade templates for public views: https://github.com/tutley/mean
This post explains how to integrate Stylus pre-processing to the MEAN stack: http://to-s.tk/integrate-stylus-to-the-mean-stack/
Short version:
Move public/css to a new assets/stylesheets and rename all the .css files to .styl
Install grunt-contrib-stylus through npm's package.json, as both a dev and runtime dependency.
-Configure stylus compilation in your Gruntfile
// ...
grunt.initConfig({
// ...
watch: {
// ...
stylus: {
files: ['assets/stylesheets/**/*.styl'],
tasks: ['stylus']
},
// ...
},
// ...
stylus: {
compile: {
options: {
paths: ['assets/stylesheets/**']
},
files: [{
dest: 'public/css/',
cwd: 'assets/stylesheets/',
src: '*.styl',
ext: '.css',
expand: true
}]
}
},
// ...
});
//...
//Load NPM tasks
// ...
grunt.loadNpmTasks('grunt-contrib-stylus');
// ...
Import views stylus files (or any substylus) in common.styl using #require statements
Remove references to views or other substylesheets in head.jade.
Then all assets/stylesheets/*.styl files should be automatically compiled into public/css/*.css, as long as grunt is running. To trigger a compile without relying on watch, you can run grunt stylus.

Incorrect path for generated coffee sourcemaps when serving with nodejs

I'm using coffeescript to do some work. The coffeescript is compiled to js with grunt and served with a simple nodejs express app.
My folder structure does follow the common one with a assets folder for things to be compiled (coffeescript, stylus) and public folder with the compiled stuff (js, css):
/
-- assets /
-- coffee /
-- lib /
-- util.coffee
-- main.coffee
-- styl
-- public /
-- css
-- js /
-- lib /
-- util.js
-- main.js
My coffee setup in grunt is:
coffee:
client:
options:
sourceMap: true
#sourceRoot: '??'
files: [
expand: true
cwd: 'assets/coffee/'
src: ['**/*.coffee']
dest: 'public/js/'
ext: '.js'
]
To serve files from the assets dir I added this to be a static directory in my express app:
app.use express.static(process.cwd() + '/assets')
Chrome correctly recognizes that the are source maps but the location to the coffee files are wrong. For example a url looks like http://localhost:3000/assets/coffee/main.coffee. Of course this results in a 404 because assets is the root for all coffee files and is served by my express app.
So I need to adjust the sourceRoot variable.
If I set sourceRoot to sourceRoot: '/assets/', Chrome generates links to http://localhost:3000/assets/main.coffee.
If I set it to sourceRoot: '/coffee/' the link is http://localhost:3000/coffee/main.coffee. This works for files in assets/coffee/. Files in a subdirectory of assets/coffee/ like assets/coffee/lib/ aren't found (the generated link is http://localhost:3000/coffee/util.coffee)
Setting the sourceRoot option seems to remove the folder structure?!
Long question short: What's the correct setting for sourceRoot? How can I preserve the folder structure?
I filed this issue as a possible bug report: https://github.com/jashkenas/coffee-script/issues/3075
This appears to actually be a bug in the CoffeeScript Grunt task.
See: https://github.com/gruntjs/grunt-contrib-coffee/blob/master/tasks/coffee.js#L87
options = _.extend({
generatedFile: path.basename(paths.dest),
sourceRoot: mapOptions.sourceRoot,
sourceFiles: mapOptions.sourceFiles
}, options);
Here, if the options object has a "sourceRoot" element, it will override the generated sourceRoot which is created by this function:
https://github.com/gruntjs/grunt-contrib-coffee/blob/master/tasks/coffee.js#L130
var createOptionsForFile = function (file, paths) {
return {
code: grunt.file.read(file),
sourceFiles: [path.basename(file)],
sourceRoot: appendTrailingSlash(path.relative(paths.destDir, path.dirname(file)))
};
};
Which uses the relative path from the destination directory to where the source file is (which would probably work if you mapped things like /js to /public/js instead, but for you there will be an extra ../ in the path).
For your case, it might work if you modified the code so that you replaced the options = _.extend({... code with something like:
var newRoot = undefined
if (options.sourceRoot) newRoot = appendTrailingSlash(path.join(options.sourceRoot, path.dirname(file)));
options = _.extend({
generatedFile: path.basename(paths.dest),
sourceRoot: mapOptions.sourceRoot,
sourceFiles: mapOptions.sourceFiles
}, options);
if (newRoot) options.sourceRoot = newRoot;
I think that will work because file should be relative to your cwd setting.
If that change to the Grunt task works, it would be worth making a cleaner version and submitting a pull request, because I think if your .coffee files are in a directory tree, that should be reflected in your sourcemaps' sourceRoot property.

Resources