I am trying to minify an angularjs application using grunt and terser. I first used uglifiy-es but then read that it has some issues. So I tried terser. But the output does not give me minified files.
The gruntfile.js
module.exports = function(grunt) {
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
//grunt task configuration will go here
ngAnnotate: {
options: {
singleQuotes: true
},
app: {
files: {
'./public/min-safe/js/_config_min.js': ['./controllers/_config.js'],
'./public/min-safe/js/ctrl_accountingdashboard.js': ['./controllers/ctrl_accountingdashboard.js'],
}
}
},
concat: {
js: { //target
src: ['./public/min/app.js', './public/min-safe/js/*.js'],
dest: './public/min/app.js'
}
},
terser: {
options: {},
files: {
'./public/min/app.js': ['./public/min/app.js'],
},
}
});
//load grunt tasks
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-terser');
grunt.loadNpmTasks('grunt-ng-annotate');
//register grunt default task
grunt.registerTask('default', ['ngAnnotate', 'concat', 'terser']);
}
I had the same problem. According to the documentation, this should work but it didn't for me. Wrapping the "files" setting in a custom target works for me:
terser: {
options: {},
main: {
files: {
'./public/min/app.js': ['./public/min/app.js'],
}
}
}
To add to #Tim's great answer:
Here is an example that allows to run grunt-terser with path / file wildcard patterns (globbing) – which it does not support out of the box.
Please note the helper properties _src and _dest in the terser config which are not read by grunt-terser itself but by the task terser_all. This task expands the globbing pattern(s) in _src and builds the real config in the files property. When done it runs terser with that updated config.
module.exports = function (grunt) {
grunt.initConfig({
terser: {
dist: {
options: {
compress: {
drop_console: true // remove console.log, console.info, ...
}
},
files: {
// FILLED through terser_all task below!
// Examples config:
// "dist/example.js": [ "path/to/files/example.js" ]
// "dist/example_joined.js": [ "path/to/files/*.js" ]
},
// -----
// HELPER PROPERTIES to build the files prop (see above) in the terser_all task below.
_src: [
"path/to/files/*.js"
],
_dest: "dist/"
}
}
});
grunt.registerTask('terser_all', function () {
// work on this target in terser config
var terser_target_name = "dist";
// read the terser config
var terser_config = grunt.config.get('terser') || {};
var terser_target_config = terser_config[terser_target_name] || {};
// get the destination dir
var destDir = terser_target_config._dest;
// loop through all source files and create an entry in the terser config for each of it
var files = grunt.file.expand(terser_target_config._src);
for (const [i, file] of files.entries()) {
grunt.log.writeln(file);
// add this file to the terser config as: dest_file: src_file
terser_target_config.files[destDir + file] = file;
}
// show new config on CLI
grunt.log.writeflags(terser_target_config);
// write back config and run task
grunt.config.set('terser', terser_config);
grunt.task.run('terser');
});
grunt.loadNpmTasks('grunt-terser');
grunt.registerTask('build', ['terser_all']);
grunt.registerTask('default', ['build']);
};
Just a note:
If you try to "disable" some options by renaming this disables the whole process. At least this was my result with grunt-terser. I was left with the original js file.
{
mangleX: {
reserved: [/* ... */]
}
}
Related
EDIT: upon further reflection I believe my question is about grunt-contrib-concat rather than sass.
I have a folder of sass files one of which is called colors.scss
//neutrals
$white: #fff;
$light-gray: #eee;
$gray: #9f9f9f;
$slate: #59595A;
$charcoal: #404041;
$gold: #FFD34E;
//define non-neutral colors by use. These are what would change if our app was whitelabeled.
$bright-accent-color: tint(#FF4849, 0%);
$muted-accent-color: $bright-accent-color;
$dark-accent-color: $bright-accent-color;
$note-color: #FFFAD5;
$bright-warning-color: black; // will this be used in new scheme?
$muted-warning-color: tint(#DB9E36, 20%);
$dark-warning-color: $charcoal;
$light-background-color: #f3f6f9;
$primary-nav-color: #172740; // dark blue
$secondary-nav-color: #263D59; // blue
I would like to produce a dozen sets of compiled css files, of which I would swap out the colors.css file for each compiled set. I'm trying to figure out how to incorporate this into my gruntfile without producing seperate tasks for each one. I would like one task that looks in folder called colors that in turn contains all of the colors.scss files and then for each one does a compilation and puts that compiled set of css files in a folder with the same name as the colors.scss file. The problem is I have no idea where to start. I'm using grunt-contrib-sass currently and I'm able to produce one set of files. My gruntfile looks like this:
sass: {
dist: {
options: {
style: 'expanded'
},
files: {
'dist/main.css': 'app/css/main.scss'
}
}
},
which works fine for compiling one set, but I want to iterate over the colors files and produce one set for each file found. is this possible? where should I start?
Think I got it. I edited my gruntfile with the following modules: sass, concat, and copy.
In summary, I concat the specific brand scss file to the main scss file and then copy all of the support files to a sass folder in the dist directory. Then I run sass on the concat'd files and output the final css files to the dist css folder.
Heres the configuration:
module.exports = function(grunt) {
'use strict';
var sassFiles = [];
grunt.loadNpmTasks('grunt-contrib-sass');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.registerTask('default', ['concat', 'copy:sass', 'sass']);
grunt.initConfig({
concat: (function(){
var concat = {
options: {
sourceMap: true
}
};
var files = [];
grunt.file.recurse('app/css/brands/', function(abspath, rootdir, subdir, filename){
files.push(filename);
});
sassFiles = files;
files.forEach(file => {
concat[file] = {
src: [
'app/css/brands/'+file,
'app/css/main.scss'
],
dest: 'dist/css/sass/'+file
};
});
return concat;
}()),
sass: {
dist: {
options: {
style: 'expanded'
},
files: (function(){
var fileObject = {};
sassFiles.forEach(file => {
var filename = file.split('.')[0];
fileObject['dist/css/'+filename+'.css'] =
'dist/css/sass/'+file;
});
return fileObject;
}())
}
},
copy: {
sass: {
files: [
{ expand: true, cwd: 'app/css', src: '**', dest: 'dist/css/sass/' }
]
}
}
});
};
In my web project I have the following directory structure
|- target
|- foo
|- bar
|- baz
I'm trying to write a Grunt task that will copy all JSON files into the target directory from the directory whose name matches a parameter provided to the build
grunt.registerTask('flavor', function(srcDir) {
var from = './' + srcDir + '/*.json';
var dest = './target/';
grunt.file.expand(from).forEach(function(src) {
grunt.file.copy(src, dest);
});
});
But when I call this with
grunt flavor:foo
I get an error
Warning: Unable to write "./target/" file (Error code: EISDIR). Use --force to continue.
As #DanielApt mentioned, you should just use grunt-contrib-copy. To build on his answer regarding on your comment about build-parameters, you can get them to the task via grunt-option.
Way one: running different target
grunt.initConfig({
copy: {
foo: {
files: [
{
'expand': 'true',
'cwd': 'foo/',
'src': [**],
'dest': 'dist/en'
}
]
},
bar: {
files: [/**/]
},
baz: {
files: [/**/]
}
}
});
var target = grunt.option("target") || "foo";
grunt.registerTask("default", ["copy:" + target]);
// run with grunt --target=foo
Way 2: Arbituary folder with templating:
var target = grunt.option("target") || "foo";
grunt.initConfig({
target: target,
copy: {
default_target: {
files: [
{
'expand': 'true',
'cwd': '<%= target %>/',
'src': [**],
'dest': 'dist/en'
}
]
},
}
});
grunt.registerTask("default", ["copy"]);
// run with grunt --target=anyfolderhere
Do you need to write your own task? If you don't I achieved this with grunt-contrib-copy.
copy: {
default: {
files: [
{
expand: true,
src: ['foo/**', 'bar/**'],
dest: 'target/'
}
]
}
}
I'm new to Grunt and I'm lost in all it's option.
I'm trying to do two things :
Minify all my js files into one each time I change one of them
Process a specific scss file when one of my scss file is changed
Here is the current Gruntfile.js I got :
module.exports = function (grunt) {
grunt.initConfig({
// define source files and their destinations
uglify: {
files: {
src: 'js/*.js', // source files mask
dest: 'jsm/', // destination folder
expand: true, // allow dynamic building
flatten: true, // remove all unnecessary nesting
ext: '.min.js' // replace .js to .min.js
}
},
watch: {
js: { files: 'js/*.js', tasks: [ 'uglify' ] },
}
});
// load plugins
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-contrib-uglify');
// register at least this one task
grunt.registerTask('default', [ 'uglify' ]);
};
How could I achieve this?
Use the grunt-contrib-concat
grunt.initConfig({
concat: {
dist: {
src: ['files/**.min.js'],
dest: 'project.js',
},
}
});
You should wrap all you files in an anonymous function and define variables using var so you do not get variable conflicts between files.
(function(){
// foo has function scope
var foo = 3;
console.log(foo);
// FOO is a global variable and
// can be accessed between files
window.FOO = 3;
console.log(FOO);
}).call()
I want to work with closure compiler, so I added grunt-closure-tools to my Grunt config, but my config is erroring with:
Verifying property closureCompiler.loader exists in config...ERROR
Here is the reference material for grunt-closure-tools:
https://www.npmjs.com/package/grunt-closure-tools
or
https://github.com/thanpolas/grunt-closure-tools
Here is my GruntFile.js:
module.exports = function(grunt) {
var path = require('path');
require('load-grunt-config')(grunt, {
//pkg: grunt.file.readJSON('package.json'),
configPath: path.join(process.cwd(), 'grunt'), //path to task.js files, defaults to grunt dir
init: true, //auto grunt.initConfig
data: { //data passed into config. Can use with <%= test %>
pkg: require('./package.json')
},
loadGruntTasks: { //can optionally pass options to load-grunt-tasks. If you set to false, it will disable auto loading tasks.
pattern: ['grunt-contrib-*', 'grunt-jslint', 'grunt-newer', 'imagemin-*','grunt-closure-tools'],
scope: 'devDependencies'
},
postProcess: function(config) {} //can post process config object before it gets passed to grunt
});
//require('load-grunt-tasks')(grunt);
grunt.registerTask("default", ["newer:jslint", "newer:concat", "closureCompiler:loader", "newer:sass"]);
};
I am using load-grunt-config to break up my config into multiple parts. Here is my closure.js file, mostly modeled on grunt-closure-tools github page example:
module.exports = {
options: {
compilerFile: '/usr/local/Cellar/closure-compiler/20141023/libexec/build/compiler.jar',
checkModified: true,
compilerOpts: {
create_source_map: null,
compilation_level: 'ADVANCED_OPTIMIZATIONS',
},
d32: true, // will use 'java -client -d32 -jar compiler.jar'
},
util: {
src: 'includes/javascript/util/util.js',
dest: 'includes/javascript/build/util.min.js'
},
loader: {
src : 'includes/javascript/loaders/loader.js',
dest: 'includes/javascript/build/loader.min.js'
}
};
Any help with this error is appreciated.
After some digging and no small amount of thrashing of teeth, I have managed to getting my config working. My Gruntfile.js:
module.exports = function(grunt) {
var path = require('path');
require('load-grunt-config')(grunt, {
//pkg: grunt.file.readJSON('package.json'),
configPath: path.join(process.cwd(), 'grunt'), //path to task.js files, defaults to grunt dir
init: true, //auto grunt.initConfig
data: { //data passed into config. Can use with <%= test %>
pkg: require('./package.json')
},
loadGruntTasks: { //can optionally pass options to load-grunt-tasks. If you set to false, it will disable auto loading tasks.
pattern: ['grunt-contrib-*', 'grunt-jslint', 'grunt-newer', 'imagemin-*','grunt-closure-tools'],
scope: 'devDependencies'
},
postProcess: function(config) {} //can post process config object before it gets passed to grunt
});
//require('load-grunt-tasks')(grunt);
grunt.registerTask("default", ["newer:jslint", "newer:concat", "closureCompiler:loader", "newer:sass"]);
};
and my closureCompiler.js file (shortened for clarity):
module.exports = {
options: {
compilerFile: '/usr/local/Cellar/closure-compiler/20141023/libexec/build/compiler.jar',
checkModified: true,
compilerOpts: {
// create_source_map: null
},
execOpts: {
maxBuffer: 999999 * 1024
},
TieredCompilation: true // will use 'java -server -XX:+TieredCompilation -jar compiler.jar'
},
loader: {
TEMPcompilerOpts: {
create_source_map: 'includes/javascript/build/loader.min.js.map'
},
src : 'includes/javascript/loaders/loader.js',
dest: 'includes/javascript/build/loader.min.js'
}
};
I had quite a few errors in my config:
I had to rename closure.js to closureCompiler.js so load-grunt-config could find the file. Oops, my bad.
The d32: true switch on the Closure Compiler means use 32-bit JVM, which I do not have on my system. I switched to TieredCompilation: true instead.
create_source_map: null in the options did not work for me. I found the reference in the source code of grunt-closure-tools but didn't spend the time to sort out what was wrong. Instead, I added TEMPcompilerOpts to the target config with the map file I wanted Closure to make.
I hope other benefit from my experience with this solution. From this point, I am going to use the ability of Closure Compiler to create source maps to merge my dev and prod build targets into a single target.
I'm trying to get grunt-contrib-watch to watch my images folder, and run grunt-imageoptim on any added/changed jpgs, gifs, or pngs.
My code below runs fine for running imageoptim on new images, as long as I don't make any new folders. Apparently this has been fixed in recent versions but I'm pretty new to Grunt so it might be something basic I'm missing. I've gotten it to see changes / new folders in watch through a less specific wildcard, but then i can't stop imageoptim from running on all images in all the folders.
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
imageoptim: {
options: {
jpegMini: false,
imageAlpha: true,
quitAfter: true
},
},
watch: {
files: ['images/**/*.jpg', 'images/**/*.png', 'images/**/*.gif'],
tasks: ['imageoptim'],
options: {
spawn: false,
},
},
});
// imageoptim
grunt.loadNpmTasks('grunt-imageoptim');
// grunt watch
grunt.loadNpmTasks('grunt-contrib-watch');
// Default task(s).
grunt.registerTask('default', ['imageoptim']);
var changedFiles = Object.create(null);
var onChange = grunt.util._.debounce(function() {
grunt.config('imageoptim.all.src', Object.keys(changedFiles));
changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', function(action, filepath) {
changedFiles[filepath] = action;
onChange();
});
};
(the bottom function is to run imageoptim on a watch change, and then prevent it from seeing it's own changes as changes to act on again)
Whoops, answered my own question :P I was previously (before posting the question) setting src in imageoptim instead of changing files in watch. This works:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
imageoptim: {
options: {
jpegMini: false,
imageAlpha: true,
quitAfter: true
},
},
watch: {
files: ['images/**'],
tasks: ['imageoptim'],
options: {
spawn: false,
},
},
});
// imageoptim
grunt.loadNpmTasks('grunt-imageoptim');
// grunt watch
grunt.loadNpmTasks('grunt-contrib-watch');
// Default task(s).
grunt.registerTask('default', ['imageoptim']);
var changedFiles = Object.create(null);
var onChange = grunt.util._.debounce(function() {
grunt.config('imageoptim.all.src', Object.keys(changedFiles));
changedFiles = Object.create(null);
}, 200);
grunt.event.on('watch', function(action, filepath) {
changedFiles[filepath] = action;
onChange();
});
};