I'm currently setting up a automated build script (with gruntjs) for a require.js driven project . Therefor I would like to run jslint/jshint on all required files before concatenating and minifying it with r.js. Since the js folder contains a lot of development files I don't want to lint, I can't just pass js/**/*.js to JSLint. My first thought was to run r.js with optimizer: 'none', lint the concatenated file and then minify it, but this is not an options for two reasons. First it will include vendor libs I don't want to lint and second finding the line with the error, find it's class, find the appropriate js-file in the dev folder, fix it there, run r.js again and finally lint it again, is way to much hassle for our workflow. So I'm looking for a possibility to hook up the linting into the r.js optimizer process or at least get a list of the requirejs dependency tree in some way, that I can parse and pass it to lint. Or any solution practicable for an automated process, you'll come up with.
Lint first, compile later. Just be specific about the files you want to lint and use the ! prefix to ignore specific files:
grunt.initConfig({
lint: {
// Specify which files to lint and which to ignore
all: ['js/src/*.js', '!js/src/notthisfile.js']
},
requirejs: {
compile: {
options: {
baseUrl: 'js/src',
name: 'project',
out: 'js/scripts.js'
}
}
}
});
// Load the grunt-contrib-requirejs module.
// Do `npm install grunt-contrib-requirejs` first
grunt.loadNpmTasks('grunt-contrib-requirejs');
// Our default task (just running grunt) will
// lint first then compile
grunt.registerTask('default', ['lint', 'requirejs']);
I prefer not overriding r.js's methods, or you might create an unwanted dependency on a specific version (you'll need to update your code should r.js change)
This is the code I use for the same purpose, making use of require's onBuildRead function and the fact that objects in javascript are passed by reference. I make sure I run the require build first, then lint the js file sources.
The downside is that you'll lint after build complete. For my setup that is not a problem.
module.exports = function(grunt) {
var jsHintOptions = {
options: {
curly: true,
eqeqeq: true,
eqnull: true,
browser: true,
globals: {
jQuery: true
}
},
all: [] // <--- note this is empty! We'll fill it up as we read require dependencies
};
var requirejsOptions = {
compile: {
options: {
paths: {
"jquery": "empty:"
},
baseUrl: "./",
name: "src/mypackage/main",
mainConfigFile: "src/mypackage/main.js",
out: 'build/mypackage/main.js',
onBuildRead: function (moduleName, path, contents) {
jsHintOptions.all.push(path); // <-- here we populate the jshint path array
return contents;
}
}
}
};
grunt.initConfig({
pkg: grunt.file.readJSON('packages/mypackage.package.json'),
requirejs: requirejsOptions,
jshint: jsHintOptions
});
// load plugin that enabled requirejs
grunt.loadNpmTasks('grunt-contrib-requirejs');
// load code quality tool
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['requirejs', 'jshint']); // <-- make sure your run jshint AFTER require
};
This answer sort of bypasses Grunt, but it should work for what you want to do. The way I would do it is look at r.js and try to override a function that receives the path to the various modules being loaded, intercept the module name, and lint the files while r.js is loading and compiling the modules. I've done it like so:
var requirejs = require('requirejs');
var options = {/*r.js options as JSON*/};
var oldNewContext = requirejs.s.newContext;
requirejs.s.newContext = function(){
var context = oldNewContext.apply(this, arguments);
var oldLoad = context.Module.prototype.load;
context.Module.prototype.load = function(){
var module = oldLoad.apply(this, arguments);
if(/\.js$/.test(this.map.url) && !/^empty:/.test(this.map.url))
console.log(this.map.url);
return module;
}
return context;
}
requirejs.optimize(options)
Then when you run requirejs.optimize on your modules, you should get all the non-empty JavaScript urls logged to the console. Instead of logging them to the console, you could use the urls to lint the files.
Instead of using the lint task, install, load, and set up grunt-contrib-jshint. It has an ignores option to ignore specific files or file path patterns.
Here's my task:
jshint: {
options: {
// options here to override JSHint defaults
boss : true, // Suppress warnings about assignments where comparisons are expected
browser : true, // Define globals exposed by modern browsers (`document`, `navigator`)
curly : false, // Require curly braces around blocks
devel : false, // Define `console`, `alert`, etc. (poor-man's debugging)
eqeqeq : false, // Prohibit the use of `==` and `!=` in favor of `===` and `!==`
"-W041" : false, // Prohibit use of `== ''` comparisons
eqnull : true, // Suppress warnings about `== null` comparisons
immed : true, // Prohibit the use of immediate function invocations w/o wrapping in parentheses
latedef : true, // Prohibit the use of a var before it's defined
laxbreak: true, // Suppress warnings about possibly unsafe line breaks
newcap : true, // Require you to capitalize names of constructor functions
noarg : true, // Prohibit the use of `arguments.caller` and `arguments.callee`
shadow : true, // Suppress warnings about var shadowing (declaring a var that's declared somewhere in outer scope)
sub : true, // Suppress warnings about using `[]` notation, e.g. `person['name']` vs. `person.name`
trailing: true, // Trailing whitespace = error
undef : false, // Prohibit the use of explicitly undeclared variables
unused : false, // Warn when you define and never use your variables
white : false, // Check JS against Douglas Crawford's coding style
jquery : true, // Define globals exposed by jQuery
// Define global functions/libraries/etc.
globals : {
amplify : true
},
ignores: [
'src/app/templates/template.js',
'src/scripts/plugins/text.min.js'
]
},
gruntfile: {
src: 'Gruntfile.js'
},
app: {
src: 'src/app/**/*.js'
},
scripts: {
src: 'src/scripts/**/*.js'
}
}
Related
UglifyJS uses commas to chain function, object and variable declarations. This is fine for productions and when the file is being minified however it makes it extremely hard to walk through the javascript with breakpoints when debugging js. I need to know how to turn this feature off in the UglifyJS Grunt Plugin.
Below is what the output looks like.
var boom = function(a) {
...
},
bing = function(b){
...
},
bam = function(c) {
...
};
This might help Gulp users using gulp-uglify :
.pipe( uglify({
compress:{
sequences:false
}
}) )
Ok I figured it out. In the the Gruntfile under options > compress add an option
sequences: false
that will stop the semi-colons being replaced with commas. You can then use breakpoints like you would normally.
uglify: {
options: {
compress: {
sequences: false
}
}
}
This might help users of HTML Minifier which uses UglifyJS under the hood:
const htmlmin = require('gulp-html-minifier-terser'); // new fork of gulp-htmlmin
.pipe(htmlmin({
collapseWhitespace: true, // etc.
minifyJS: {compress:{sequences:false}},
});
I found you can pass through Uglify options via the minifyJS instead of just true which uses all the defaults.
I am using r.js with uglify to minify and concatenate my scripts. I am getting some errors on a production site where the stack trace returned is unintelligible. I would like to temporarily turn off the mangling of function names (variable names are fine) and am having trouble working out how to do this as r.js wraps the configuration options that are passed to uglify.js
The uglify config section in my r,js build config looks like this
uglify: {
beautify: true,
indent_start: 0,
indent_level: 1,
}
I would like to add the
-nmf or --no-mangle-functions – in case you want to mangle variable names, but not touch function names. (from here)
If i add the line
uglify: {
beautify: true,
indent_start: 0,
indent_level: 1,
'--no-mangle-functions': true
}
It does nothing, as does 'no-mangle-functions': true.
How do I pass this option to uglify?
Trying to make uglified/mangled code readable kind of defeats it's purpose in the first place.
Probably, what you are after are source maps.
To generate source maps in Uglify just add these options:
uglify: {
options: {
sourceMap: 'build.min.map',
sourceMappingURL: 'build.min.map'
}
}
Map filename must mirror the final javascript filename:
uglified.js <=> uglified.map
From what i can see in the source code of r.js there is no direct differentiation between functions and variable names. But there is an option called no_functions which is actually passed to the uglify section where the default value is false
Passing of options:
https://github.com/jrburke/r.js/blob/master/dist/r.js#L25067
Defaulting no_functionsto false:
https://github.com/jrburke/r.js/blob/master/dist/r.js#L11492
I cannot test it right now, so i am only guessing. Maybe you can try this option
I'm using r.js to build a production script for my Require JS application and I'm looking for a way to either use an alternate compression library or change the defaults used. I want whitespace to be removed but for variable names to remain the same.
I have a particular requirement to retain variable names as they are and not have them altered. The need for constant variable names introduces a little 'code smell' but it makes the application's configuration file more robust against non-expert editors - so please try to avoid suggesting a design change here.
I currently have r.js configured to not optimise the JavaScript at all, which means not only are variable names retained but also whitespace. The relevant piece from gruntfile.js is provided below.
Can anyone suggest a way to compress whitespace but not change variable names in an r.js build?
english: {
options: {
baseUrl: "js",
mainConfigFile: "js/app-en.js",
name: "app-en",
out: "js/dist/<%= pkg.name %>-en.js",
optimize: "none"
}
}
The r.js optimizer has settings you can use to control how the minifier operates. The default minifier used is UglifyJS. The uglifyjs option tells r.js how to invoke it. Given the settings you've shown, removing optimize: "none" and adding uglify: { no_mangle: true } is what is needed:
english: {
options: {
baseUrl: "js",
mainConfigFile: "js/app-en.js",
name: "app-en",
out: "js/dist/<%= pkg.name %>-en.js",
uglify: {
no_mangle: true
},
}
}
The whole set of settings that UglifyJS takes is documented here. If you ever need or want to switch to UglifyJS2 or Closure, r.js has uglify2 and closure settings that you can use to set their options.
For Uglify2, the setting to prevent mangling would be:
uglify2: {
mangle: false
}
With Closure, I believe you'd want:
closure: {
CompilationLevel: 'WHITESPACE_ONLY',
},
What is the best way to remove asserts (console.assert) from JavaScript code for production version? Maybe there is some software that can be configured to sort of build JavaScript and remove asserts?
UPDATE __________________________
I've installed GRUNT and groundskeeper plugin. This is what my Gruntfile.js has:
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
groundskeeper: {
compile: {
files: {
'calculator/add.js': 'calculator/add.js'
},
options: {
console: false
}
}
}
});
// These plugins provide necessary tasks.
grunt.loadNpmTasks('grunt-groundskeeper');
The problem is with when I run grunt groundskeeper I get the following error:
Running "groundskeeper:compile" (groundskeeper) task
Warning: Line 2: Invalid left-hand side in assignment Use --force to continue.
I assume that the problem is with this line:
'calculator/add.js': 'calculator/add.js'
Since if I replace it with the following:
'path/to/result.js': 'path/to/source.js'
Everything works fine:
Running "groundskeeper:compile" (groundskeeper) task
>> File path/to/result.js created empty, because its counterpart was empty.
>> 1 files read, 0 cleaned.
What's wrong with the my original line?
make assert as an empty function in your production
console.assert = function(){}
if you can check it is a production version then put the code like,
if (production) {
console.assert = function(){}
}
for old IE shim
if (typeof console == "undefined" || typeof console.assert == "undefined"){
var console = { assert : function() {} };
}
I am using the grunt-groundkeeper plugin for this:
https://github.com/Couto/grunt-groundskeeper
If you're not using Grunt yet, I really recommend using it for JavaScript projects. Explaining how to use Grunt itself is out of scope of this question.
The following example config sets removing all logging statements in the www/scripts.min.js file as the default task.
'use strict';
module.exports = function(grunt) {
grunt.initConfig({
groundskeeper: {
dist: {
files: {
'www/scripts.min.js': 'www/scripts.min.js'
}
}
});
grunt.loadNpmTasks('grunt-groundskeeper');
grunt.registerTask('default', ['groundskeeper']);
};
I'm working on an angular application that is written in CommonJS syntax and uses a grunt task with the grunt-contrib-requirejs task to translate the source files to AMD format and compile it into one output file. My goal is to make Karma work with RequireJS and keep my source files and spec files in CommonJS syntax.
I've been able to get a simple test passing in AMD format with the following file structure:
-- karma-test
|-- spec
| `-- exampleSpec.js
|-- src
| `-- example.js
|-- karma.conf.js
`-- test-main.js
and the following files:
karma.conf.js
// base path, that will be used to resolve files and exclude
basePath = '';
// list of files / patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
REQUIRE,
REQUIRE_ADAPTER,
'test-main.js',
{pattern: 'src/*.js', included: false},
{pattern: 'spec/*.js', included: false}
];
// list of files to exclude
exclude = [];
// test results reporter to use
// possible values: 'dots', 'progress', 'junit'
reporters = ['progress'];
// web server port
port = 9876;
// cli runner port
runnerPort = 9100;
// enable / disable colors in the output (reporters and logs)
colors = true;
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel = LOG_DEBUG;
// enable / disable watching file and executing tests whenever any file changes
autoWatch = true;
// Start these browsers, currently available:
browsers = ['Chrome'];
// If browser does not capture in given timeout [ms], kill it
captureTimeout = 60000;
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = false;
example.js
define('example', function() {
var message = "Hello!";
return {
message: message
};
});
exampleSpec.js
define(['example'], function(example) {
describe("Example", function() {
it("should have a message equal to 'Hello!'", function() {
expect(example.message).toBe('Hello!');
});
});
});
test-main.js
var tests = Object.keys(window.__karma__.files).filter(function (file) {
return /Spec\.js$/.test(file);
});
requirejs.config({
// Karma serves files from '/base'
baseUrl: '/base/src',
// Translate CommonJS to AMD
cjsTranslate: true,
// ask Require.js to load these files (all our tests)
deps: tests,
// start test run, once Require.js is done
callback: window.__karma__.start
});
However, my goal is to write both the source file and the spec file in CommonJS syntax with the same results, like so:
example.js
var message = "Hello!";
module.exports = {
message: message
};
exampleSpec.js
var example = require('example');
describe("Example", function() {
it("should have a message equal to 'Hello!'", function() {
expect(example.message).toBe('Hello!');
});
});
But despite having the cjsTranslate flag set to true, I just receive this error:
Uncaught Error: Module name "example" has not been loaded yet for context: _. Use require([])
http://requirejs.org/docs/errors.html#notloaded
at http://localhost:9876/adapter/lib/require.js?1371450058000:1746
Any ideas on how this can be accomplished?
Edit: I found this issue for the karma-runner repo: https://github.com/karma-runner/karma/issues/552 and there's a few comments that may help with this problem, but I haven't had any luck with them so far.
The solution I ended up finding involved using grunt and writing some custom grunt tasks. The process goes like this:
Create a grunt task to build a bootstrap requirejs file by finding all specs using a file pattern, looping through them and building out a traditional AMD style require block and creating a temporary file with code like this:
require(['spec/example1_spec.js'
,'spec/example2_spec.js',
,'spec/example3_spec.js'
],function(a1,a2){
// this space intentionally left blank
}, "", true);
Create a RequireJS grunt task that compiles the above bootstrap file and outputs a single js file that will effectively include all source code, specs, and libraries.
requirejs: {
tests: {
options: {
baseUrl: './test',
paths: {}, // paths object for libraries
shim: {}, // shim object for non-AMD libraries
// I pulled in almond using npm
name: '../node_modules/almond/almond.min',
// This is the file we created above
include: 'tmp/require-tests',
// This is the output file that we will serve to karma
out: 'test/tmp/tests.js',
optimize: 'none',
// This translates commonjs syntax to AMD require blocks
cjsTranslate: true
}
}
}
Create a grunt task that manually starts a karma server and serve the single compiled js file that we now have for testing.
Additionally, I was able to ditch the REQUIRE_ADAPTER in the karma.conf.js file and then only include the single compiled js file instead of the patterns that matched all source code and specs, so it looks like this now:
// base path, that will be used to resolve files and exclude
basePath = '';
// list of files / patterns to load in the browser
files = [
JASMINE,
JASMINE_ADAPTER,
REQUIRE,
'tmp/tests.js'
];
// list of files to exclude
exclude = [];
// test results reporter to use
// possible values: 'dots', 'progress', 'junit'
reporters = ['progress'];
// web server port
port = 9876;
// cli runner port
runnerPort = 9100;
// enable / disable colors in the output (reporters and logs)
colors = true;
// level of logging
// possible values: LOG_DISABLE || LOG_ERROR || LOG_WARN || LOG_INFO || LOG_DEBUG
logLevel = LOG_INFO;
// enable / disable watching file and executing tests whenever any file changes
autoWatch = true;
// Start these browsers, currently available:
browsers = ['PhantomJS'];
// If browser does not capture in given timeout [ms], kill it
captureTimeout = 60000;
// Continuous Integration mode
// if true, it capture browsers, run tests and exit
singleRun = true;
In the grunt task configuration for the requirejs compilation, it was also necessary to use almond in order to start the test execution (test execution would hang without it). You can see this used in the requirejs grunt task config above.
There's a couple things. First of all: I might have missed some details in your question (as it is super huge) - so sorry about that.
In short, you may want to checkout Backbone-Boilerplate wip branch testing organization: https://github.com/backbone-boilerplate/backbone-boilerplate/tree/wip
First: RequireJS does not support unwrapped raw common.js module. cjsTranslate is a R.js (the build tool) option to convert Commonjs to AMD compatible during build. As so, requiring a CJS raw module won't work. To resolve this issue, you can use a server to filter the scripts sent and compile them to AMD format. On BBB, we pass file through a static serve to compile them:
karma proxies setting: https://github.com/backbone-boilerplate/backbone-boilerplate/blob/wip/Gruntfile.js#L232-L234
Server setting: https://github.com/backbone-boilerplate/backbone-boilerplate/blob/wip/Gruntfile.js#L173-L179
Second: The Karma requirejs plugin isn't working super well - and it's somehow easy to use requireJS directly. On BBB, that's how we managed it: https://github.com/backbone-boilerplate/backbone-boilerplate/blob/wip/test/jasmine/test-runner.js#L16-L36
Hope this helps!