Wrong grunt template string evaluation result - gruntjs

When using the following Grunt Configuration, I get 404 errors.
module.exports = function(grunt) {
var taskConfig = {
app: 'app',
dist: 'app'
};
grunt.initConfig({
taskConfig: taskConfig,
connect: {
serveMyApp: {
options: {
keepalive: true,
debug: true,
port: '3001',
hostname: '0.0.0.0',
livereload: true,
base: '<%= taskConfig.dist %>/',
middleware: function(connect, options, middlewares) {
// 1. mod-rewrite behavior
var rules = [
'!\\.html|\\.js|\\.css|\\.ico|\\.svg|\\.jp(e?)g|\\.png|\\.woff|\\.gif$ /index.html'
];
middlewares.unshift(rewrite(rules));
return middlewares;
}
}
}
}
);
If I would set base to a string value as follows, connect works as expected:
base: 'app/',
I was expecting the template string evaluation to generate the same string value for base, but I guess it doesn't.
What I am doing wrong?

the <%= ... %> syntax is for templating.
within your Gruntfile.js just call the variable like you would do it within any other js-file
...
base: taskConfig.dist,
...

Related

Create matching file names in Grunt

I've got several projects that each use an identical Gruntfile to run tasks and put the output in their own dist folder. Folder setup:
MyProjects
- Project1
- src
- dist
- Project2
- src
- dist
.....
I can't figure out how to run Grunt at the top level (MyProjects) and still have the output generated in the correct dist folder dynamically.
Is there a way I can have Grunt put the output in the correct dist folder without having to hard code it into the Gruntfile? Something like:
dist: {
files: {
// destination : source js
'<% ProjectName %>/dist/app.js': '<% ProjectName %>/src/app.js'
},
Thanks
module.exports = function (grunt) {
require('load-grunt-tasks')(grunt);
// Project configuration.
grunt.initConfig({
watch: {
scripts: {
files: ['src/**/*.js'],
tasks: ['browserify', 'file_append', 'concat'],
options: {
spawn: false
}
},
sass: {
files: "src/scss/*.scss",
tasks: ['sass', 'file_append', 'concat']
}
},
sass: {
dist: {
options: {
style: 'expanded'
},
files: {
// destination // source file
"format/css/styles.css": "src/scss/styles.scss"
}
},
options: {
sourcemap: "none",
style: "compact",
noCache: true
}
},
file_append: {
default_options: {
files: [
// Development build
{
append: "",
prepend: "",
input: "format/app.js",
output: "format/dev.app.js"
},
{
append: "</style>`)",
prepend: "document.body.insertAdjacentHTML('afterbegin', `\n<style>\n",
input: "format/css/styles.css",
output: "format/css/dev.styles.html"
},
// Production build
{
append: "</script>",
prepend: "<script>\n",
input: "format/app.js",
output: "format/prod.app.html"
},
{
append: "</style>",
prepend: "<style>\n",
input: "format/css/styles.css",
output: "format/css/prod.styles.html"
}
]
}
},
concat: {
options: {
seperator: '\n'
},
// Development build
dev: {
src: ['format/dev.app.js', 'format/css/dev.styles.html'],
dest: 'dev/dev.app.js'
},
// Production build
prod: {
src: ['format/prod.app.html', 'format/css/prod.styles.html'],
dest: 'dist/prod.app.html'
}
},
browserify: {
dist: {
files: {
// destination for transpiled js : source js
'format/app.js': 'src/app.js'
},
options: {
transform: [
[
'babelify', {
presets: "es2015",
comments: false,
plugins: "transform-object-rest-spread"
}
]
],
browserifyOptions: {
debug: false
}
}
}
}
});
grunt.registerTask('default', [
'sass',
'browserify:dist',
'file_append',
'concat',
'watch'
]);
};
There's a couple ways you can tackle this.
One option is to overload the arguments you pass to the task & include the folder name you wish to target.
grunt sass:dist:Project1
The additional argument is accessible via lodash templates which are a part of the GruntJS framework, and allows the configuration to be set at the time the task is ran:
sass: {
dist: {
options: {
style: 'expanded'
},
files: {
// destination // source file
"MyProjects/<%= grunt.task.current.args[0] %>/format/css/styles.css": "MyProjects/<%= grunt.task.current.args[0] %>/src/scss/styles.scss"
}
},
options: {
sourcemap: "none",
style: "compact",
noCache: true
}
}
This approach works in the context of the function that's executing, but it wouldn't continue to pass the args to the next task. To do that, we need to add a custom task which will set a configuration object before executing the task list:
grunt.registerTask("build", (project) => {
const buildConfig = { project };
grunt.config.set("build", buildConfig);
grunt.task.run([
'sass',
'browserify:dist',
'file_append',
'concat',
'watch'
]);
});
Now when we run grunt build:Project1, your custom task build will run and set the property we passed in the grunt config object. We can then reference that value in our other grunt config objects using lodash like we did for the first option. To access config values with lodash templates, we just have to provide the config pointer in json notation:
files: {
"MyProjects/<%= build.project %>/format/css/styles.css": "MyProjects/<%= build.project %>/src/scss/styles.scss"
}
Grunt compiles the configs required for a task at the time they're run & will process the lodash templates then, allowing you to inject your project name into a task. Since we stored the value in the config object, the value will persist through until grunt completes and exits.

grunt-contrib-connect running but not working

I am using : https://github.com/gruntjs/grunt-contrib-connect
"grunt-contrib-connect": "^1.0.2",
After run: grunt connect
Running "connect:server" (connect) task
Started connect web server on http://localhost:8000
Done.
So when I check in my browser: localhost:8000 , does not open anything.
can someone help on this?
gruntfile pastenbin : http://pastebin.com/nL771d5j
Gruntfile.js
module.exports = function (grunt) {
var config = {};
//setup the configuration object
var jshint;
//all tasks that must be loaded.
var tasks = [
,'grunt-contrib-watch'
,'grunt-contrib-concat'
,'grunt-contrib-sass'
,'grunt-contrib-connect'
];
//src ===============================
var src;
config.src = src = {
sassMain : 'scss/main.scss',
distFolder : 'public/stylesheets/dist.css',
devFolder : 'public/stylesheets/dev.css',
sassFolder : 'scss/**/*.scss',
serverPort: 9000,
serverHost: '0.0.0.0'
};
//Concat ===============================
var concat
config.concat = concat = {};
concat.dev = {
files: {
"public/myapp.development.js": [
"with-bootstrap/public/js/vendor"
,"with-bootstrap/public/js/**/*.js"
]
}
};
//Watch ===============================
config.watch = {
scripts: {
files: ["<%= src.sassFolder %>"]
,tasks: ["sass:dist"]
}
}
//Sass ===============================
var sass;
config.sass = sass = {};
//distribution
sass.dist = {
options: {
style: "compressed",
noCache: true,
sourcemap: 'none',
update:true
}
, files: {
"<%= src.distFolder %>" : "<%= src.sassMain %>"
}
};
//development env.
sass.dev = {
options: {
style: "expanded",
lineNumber: true,
}
, files: {
"<%= src.devFolder %>" : "<%= src.sassMain %>"
}
};
//grunt serve ===============================
config.connect = {
server: {
options: {
port: 8000,
base: {
path: 'SITE',
options: {
index: 'index.html',
maxAge: 300000
}
}
}
}
};
//Register custom tasks ===============================
grunt.registerTask('default',['dev']);
grunt.registerTask('dev', ['concat:dev','sass:dev']);
grunt.registerTask('dist',['concat:dev','sass:dist']);
//General setup ===============================
grunt.initConfig(config);
tasks.forEach(grunt.loadNpmTasks);
};
If you're running the grunt-contrib-connect plugin by itself, you'll need the property keepalive set to true in your grunt config:
config.connect = {
server: {
options: {
port: 8000,
keepAlive: true,
base: {
path: 'SITE',
options: {
index: 'index.html',
maxAge: 300000
}
}
}
}
};
Worth Noting if you want to use your connect config as part of a chain of tasks, keepAlive will need to be set to false, otherwise tasks after connect task won't run. You can also keep connect running by pairing it with the watch task w/o using the keepAlive option.

Restart node and run tests

Does anyone know the best solution for automatically restarting node and running tests after the restart every time a file changes?
I am currently using grunt-contrib-watch with grunt-develop. I am getting an ECONNREFUSED error on the some restarts. I think it is because my tests are running before the server is fully online.
Any ideas on how best to achieve what I want?
What I want: Restart node and then run all integration tests after each file change.
I am taking a BDD approach to testing (as opposed to regular unit tests) with cucumber.js. I wanted to make sure that each test run against the API I was building started on a fresh boot-up of the application.
I figured it out. Here is what I used:
grunt-contrib-watch to monitor for file changes.
It in turn calls
grunt-develop to restart the application
grunt-cucumberjs to run the cucumber tests
I then modified my index.js (starts the app) so that it doesn't start the app if the NODE_ENV is set to test. That way the cucumber tests actually start the server and can wait till the start process has finished before running the tests.
Here is the GruntFile and Index file:
Gruntfile.js
/*jslint node: true */
"use strict";
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
env: {
dev: {
APP_DIR_FOR_CODE_COVERAGE: 'coverage/instrument/',
NODE_ENV: 'dev',
PORT: 8080
},
test: {
APP_DIR_FOR_CODE_COVERAGE: 'coverage/instrument/',
NODE_ENV: 'test',
PORT: 8081
},
coverage: {
APP_DIR_FOR_CODE_COVERAGE: 'coverage/instrument/',
NODE_ENV: 'test',
PORT: 8081
}
},
watch: {
js: {
files: [
'index.js',
'features/**/*.js',
'server/**/*.js'
],
tasks: ['develop', 'cucumberjs', 'jshint'],
options: {
nospawn: true
}
}
},
jshint: {
all: ['Gruntfile.js', 'index.js', 'server/**/*.js', 'features/**/*.js']
},
nodemon: {
dev: {
script: 'index.js'
}
},
cucumberjs: {
src: './features',
},
develop: {
server: {
file: 'index.js'
}
},
instrument: {
files: ['index.js', 'server/**/*.*'],
options: {
lazy: true,
basePath: 'coverage/instrument/'
}
},
storeCoverage: {
options: {
dir: 'coverage'
}
},
makeReport: {
src: 'coverage/coverage.json',
options: {
type: 'lcov',
dir: 'coverage/reports',
print: 'detail'
}
},
coverage: {
options: {
thresholds: {
'statements': 90,
'branches': 90,
'lines': 90,
'functions': 90
},
dir: 'coverage',
root: ''
}
}
});
grunt.loadNpmTasks('grunt-env');
grunt.loadNpmTasks('grunt-contrib-watch');
grunt.loadNpmTasks('grunt-develop');
grunt.loadNpmTasks('grunt-cucumber');
grunt.loadNpmTasks('grunt-istanbul');
grunt.loadNpmTasks('grunt-istanbul-coverage');
grunt.loadNpmTasks('grunt-nodemon');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.registerTask('default', ['env:dev', 'nodemon']);
grunt.registerTask('test', ['env:test', 'watch']);
grunt.registerTask('testcoverage', ['env:test', 'jshint', 'instrument', 'cucumberjs', 'storeCoverage', 'makeReport', 'coverage']);
};
Index.js
/*jslint node: true */
"use strict";
var Hapi = require('hapi');
var Good = require('good');
var server = {};
exports.server = {
start: function(callback) {
/* istanbul ignore next */
var port = process.env.PORT || 8080;
server = new Hapi.Server(port);
var routes = require('./server/routes');
routes.register(server);
var exceptionHandling = require('./server/exceptionHandling');
exceptionHandling.register(server);
server.pack.register(Good, function(err) {
/* istanbul ignore if */
if (err) {
throw err; // something bad happened loading the plugin
}
/* istanbul ignore next */
server.log('info', 'Server starting at ' + server.info.uri);
server.start(callback);
});
},
stop: function(callback) {
server.log('info', 'Server stopping.');
server.stop(null, callback);
},
rootUrl: function() { return server.info.uri; }
};
/* istanbul ignore if */
if (process.env.NODE_ENV != 'test') {
exports.server.start(function() {});
}

How do I include the filename in an uglify banner in grunt?

I am using grunt to minify some JS files. It would be nice to include the filename in the banner. I have found several examples how to include the package name in the banner, but I haven't yet managed to get the filename in there. So: What do I have to put in the gruntfile (see below) instead of pkg.name to get the source filename (or the dest filename) in there?
Thanks in advance
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
files: [{
expand: true,
src: '**/*.js',
dest: 'build',
cwd: 'src',
ext: '.min.js'
}]
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
// Default task(s).
grunt.registerTask('default', ['uglify']);
};
I tried to do the same thing, and couldn't find any reference to the current filename that was exposed to the template. Here's the workaround I finally figured out; it's a custom task that dynamically creates a new target for each file:
grunt.registerMultiTask('minify', function () {
this.files.forEach(function (file) {
var path = file.src[0],
target = path.match(/src\/(.*)\.js/)[1];
// store some information about this file in config
grunt.config('ugtargets.' + target, {
path: path,
filename: path.split('/').pop()
});
// create and run an uglify target for this file
grunt.config('uglify.' + target + '.files', [{
src: [path],
dest: path.replace(/^src\/(.*)\.js$/, 'build/$1.min.js')
}]);
grunt.task.run('uglify:' + target);
});
});
And my uglify config:
uglify: {
options: {
banner: 'Filename: <% ugtargets[grunt.task.current.target].filename %>\n'
}
}
For each file in your source directory, we create a target name out of the filename. The exact process will depend on how your files are named; you'll want to strip out any dots or slashes. Here I used a regex; in my app I actually used fs to read some JSDoc data from the files themselves.
Then we store that filename in an object in the Grunt configuration, indexed by the target name. Here I used an object with a couple of properties; you could add more stuff here or just use a plain string if you wanted.
Finally we add a target configuration to the uglify config. This is just the src and dest for the current file; we have to do our own filename processing but it's not much work. Then we run the uglify task with the new target.
In the banner template, you can now use grunt.task.current.target to grab the data we stored in the config earlier. Presto!
In case you have more scripts to be uglify is convenient to separate it to particular subtasks with custom property as name identifier.
uglify: {
options: {
banner:
'<% var subtask = uglify[grunt.task.current.target]; %>' +
'/* <%= subtask.name %> <%= pkg.version %> (<%= grunt.template.today("yyyy-mm-dd, HH:MM") %>)\n' +
' */\n'
},
main: {
name: 'main.min.js',
files: [{
src: 'build/files/js/main.min.js',
dest: 'build/files/js/main.min.js'
}]
},
vendor: {
name: 'vendor.min.js',
files: [{
src: 'build/files/js/vendor.min.js',
dest: 'build/files/js/vendor.min.js'
}]
}
}
Try the following:
banner: grunt.file.read('./folder/file.js'),
https://stackoverflow.com/questions/38854998/dynamic-mapping-and-concat-with-grunt-uglify/
https://github.com/mattstyles/grunt-banner/issues/5#issuecomment-33445038
process: function (src, filepath) {} did the job.
For me, I wanna add "//# sourceUrl=xxx.min.js" at the bottom of each uglified .min.js so that I can debug these dynamically loaded .min.js. The following simple Gruntfile.js works for me:
module.exports = function (grunt) {
var cwd = "/Branding/Layouts/JS/";
var src = [
"file1.js",
"file2.js",
"file3.js"
];
var minified_src = [];
for (i=0; i< src.length; i++)
minified_src.push(src[i].replace(/\.js$/g, ".min.js"));
var config = grunt.initConfig({
"uglify": {
options: {
sourceMap: true
},
target: {
files: [
{
expand: true,
cwd: cwd,
src: src,
dest: cwd,
ext: ".min.js",
extDot: "last"
}
]
}
},
concat: {
options: {
process: function (src, filepath) {
return src + "\n//# sourceURL=" + filepath.split("/").slice(-1);
}
},
target: {
files: [
{
expand: true,
cwd: cwd,
src: minified_src,
dest: cwd
}
]
}
}
});
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('default', ['uglify', 'concat']);
};
Note: uglify can't preserve a comment that is not inside a code block (like //# sourceMap=xxx.js), I have to use concat to append the comment after uglify is done.
Wow, this is my first stackoverflow post.
Documentation is indeed scarse, but <%= pkg.name %> in the banner string implies you can also do <% for ( var s in grunt) { %> \ngrunt.<%=s%><% } %> or even <% for ( var s in this) { %> \nthis.<%=s%><% } %>.
So (after some searching) to get the filename you can do this:
var bannerTemplate = '<%'
+' var subtask = uglify[grunt.task.current.target];'
+' var file = subtask?subtask.dest:\'\';'
+' var filename = file.split(\'/\').pop();'
+'%>'
+'/*! <%= filename %>'
+'\n * version: <%= pkg.version %>'
+'\n * author: <%= pkg.author %>'
+'\n */\n';

Using Grunt to set environment based variables

I'm looking at grunt-string-replace to accomplish changing variables in my files to an environment specific variable. However, when I try to run the grunt script below I get this warning: "Task min:dev not found". But in this code I have that defined:
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
min: {
dev: {
"string-replace": {
dist: {
files: {
"/newrelic.js": "/newrelic.js"
},
options: {
replacements: [
{
pattern: /$APPNAME/ig,
replacement: "services"
},
{
pattern: /$ENV/ig,
replacement: "nonprod"
}
]
}
}
}
},
prod: {
pkg: grunt.file.readJSON('package.json'),
uglify: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-string-replace');
// Default task(s).
grunt.registerTask('default', ['min:dev']);
grunt.registerTask('prod', ['min:prod']);
};
Am I missing something?
Have you read the manual? The getting started guide states that the tasks expect their configuration to be specified in a property of the same name. You have no 'min' task defined, thus it's erroring.
The top-level keys of the config object need to match the task names, and then you define 'targets' (eg, 'dev' or 'prod') within the task config.
So, here's your Gruntfile re-worked. I have not tested this so I can't promise it will work, but it will certainly lead you in the right direction:
module.exports = function (grunt) {
// Project configuration.
grunt.initConfig({
pkg: grunt.file.readJSON('package.json'),
"string-replace": {
dev: {
files: {
"/newrelic.js": "/newrelic.js"
},
options: {
replacements: [{
pattern: /$APPNAME/ig,
replacement: "services"
}, {
pattern: /$ENV/ig,
replacement: "nonprod"
}]
}
}
},
uglify: {
prod: {
options: {
banner: '/*! <%= pkg.name %> <%= grunt.template.today("yyyy-mm-dd") %> */\n'
},
build: {
src: 'src/<%= pkg.name %>.js',
dest: 'build/<%= pkg.name %>.min.js'
}
}
}
});
// Load the plugin that provides the "uglify" task.
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-string-replace');
// Default task(s).
grunt.registerTask('default', ['string-replace:dev']);
grunt.registerTask('prod', ['uglify:prod']);
};
think your trying to do this, this fragment will put the environment variable SOME_ENV, into the grunt config, the replace is simply giving you a easy way to do ${SOME_ENV} placeholders in files for replacement
module.exports = function(grunt) {
// Project configuration.
grunt.initConfig({
pkg : grunt.file.readJSON('package.json'),
SOME_ENV: process.env.SOME_ENV,
copy: {
files: [
{expand: true, cwd: "src/", src: ["*.*", "**/*.*"], dest: "./build",}
],
options: {
// Replace ${MEH} in files with grunt.config.get("MEH"),
// eg ${SOME_ENV}
// == grunt.config.get("SOME_ENV")
// == process.env.SOME_ENV == systems SOME_ENV environment var
process: function (content, srcpath) {
return content.replace(
/\$\{([a-zA-Z.]+)\}/g,
function replacer(match, p1, offset, string){
var value = grunt.config.get(p1);
grunt.log.write(" in file '" + srcpath + "'\n replacing '" + match + "'\n with '" + value + "'\n");
return value;
});
}
},
}
});
// Production Build Tools
grunt.loadNpmTasks('grunt-contrib-copy');
// Default Production Build task(s).
grunt.registerTask('default', ['copy']);
};

Resources