I am trying to use grunt replace to change the names of files and add a random number to the file to prevent caching of images, css and js files.
So I am running the following code
module.exports = function replace(grunt) {
var randomVersion = ((new Date()).valueOf().toString()) + (Math.floor((Math.random() * 1000000) + 1).toString());
var replace = {
options: {
variables: {
'randomVersion': randomVersion
},
overwrite: true
},
files: [{
src: './target/dist/index.html',
dest: './target/dist/index.' + randomVersion + '.html'
}]
};
console.log(randomVersion);
grunt.config.set('replace', replace);
};
but all I get back is a "Destination is not defined
Can anyone shed any light on this?
Thanks
I am not completely certain about what your code is trying to achieve, but I will proceed to answer according to my understanding.
First, are you trying to use the grunt-replace library? If so, I think this is incorrect. This library is for replacing values within the contents of source files, and not the file paths themselves.
It looks to me that what you want to do is to copy your source index.html file to a file path with a unique identifier in it. You may be trying to wrap this in a custom task, but doing so is not necessary.
The following is a sample Gruntfile.js of how I would achieve this task. Note that this solution requires the installation of the grunt-contrib-clean and grunt-contrib-copy libraries.
module.exports = function (grunt) {
grunt.initConfig({
clean: {
dist: ['./target/dist/index.*.html']
},
copy: {
dist: {
src: './target/src/index.html',
dest: './target/dist/index.<%= Date.now() %>.html'
}
}
});
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.loadNpmTasks('grunt-contrib-copy');
grunt.registerTask('default', ['clean', 'copy']);
};
There are a few things about this solution that are worth pointing out:
The index.html file is not in the /dist folder because it is not
intended for distribution, but is, rather, a source.
Before creating new versions of index.*.html, we will delete any
existing versions in the /dist folder so that we don't have an
accumulation of each version.
Instead of using a "random" number for our versions, we will use a
timestamp. This is helpful because each version will have a greater
number than the one before it.
Related
In my js files I have references to HTML files, like window.location. I would like grunt cache bust to update that reference and add the hash data, so the loaded page is the right one, the one that uses the right versioned file. For example:
window.location = 'myweb.html'; > window.location = 'myweb.html?575a2aa1158af941?575a2aa9658af941';
I can't find any configuration of the cache bust that allows me to write within the js file. In my Gruntfile.js I have added the assets and the scr files that must be written, without success.
I can't find any configuration of the cache bust that allows me to write within the js file
...I also couldn't get it to do that.
Finally, I opted for a custom grunt solution to achieve this. This entailed:
Utilizing a node package called randomstring to generate my own random string.
$ npm install randomstring --save-dev
Setting the random string generated as the value of options.hash in my cacheBust task.
Utilizing grunt-text-replace to search the .js file/s for '.html' and replacing any instances found with the newly generated random string plus '.html'. E.g. '.a5G5p7QdOE6DF1St4k.html'.
$ npm install grunt-text-replace --save-dev
Gruntfile.js
module.exports = function(grunt) {
var randomstring = require("randomstring");
grunt.initConfig({
randomString: randomstring.generate(),
cacheBust: {
myTarget: {
options: {
// <-- Your options here
hash: '<%= randomString %>' //<-- This template references the random generated string.
},
src: [/* Your settings here */]
}
},
replace: {
js: {
src: './src/**/*.js',
dest: './dist/', //<-- creates a copy
replacements: [{
from: /\.html'/, // matches all instances of .html'
to: '.<%= randomString %>.html\'' //<-- Note the dot separator at the start.
}]
}
}
});
require('load-grunt-tasks')(grunt);
grunt.registerTask('myCacheBust', ['cacheBust:myTarget', 'replace:js']);
grunt.registerTask('default', ['myCacheBust']);
};
Notes:
Any path references in the gist above will be need to be updated according to your project directory.
load-grunt-tasks is also used in the gist above:
$ npm install load-grunt-tasks --save-dev
The regex used in the replace:js task searches for all instances of the characters .html' in the .js files.
You can specify the no. of characters that appear in the random generated string by passing the value in as an argument. E.g. randomstring.generate(7)
I worked on a project which used Grunt cache bust to bust filenames in JS files. The configuration looked like this
cacheBust : {
revProd: {
options: {
assets: ['**/*.js', '!assets/js/config.constant.js','**/*.css','!assets/css/themes/*.css'],
baseDir: 'standardversion',
deleteOriginals: true,
jsonOutput: true, // Output the original => new URLs to a JSON file
jsonOutputFilename: 'grunt-cache-bust.json'
},
src: ['standardversion/index.html', 'standardversion/assets/js/config.contants.js']
}
Where my config.contants.js file has paths like
'propertiesCtrl': 'assets/views/properties/controllers/properties.controller.js',
'propertyDetailsCtrl': 'assets/views/properties/controllers/propertyDetails.controller.js',
'propertyAddCtrl': 'assets/views/properties/controllers/addProperty.controller.js',
You can bust HTMLs by adding **/*.html to assets option
I've had a similar situation, and I solved by adapting the code above from RobC.
To avoid problems with cache when deploying, I added a hash after the html reference. By doing so, you force the browser to load the files after deployment, but after that, the files can be cached without problems.
Here's my code.
module.exports = function(grunt) {
var randomstring = require("randomstring");
grunt.initConfig({
randomString: randomstring.generate(),
replace: {
js: {
src: './src/**/*.js',
dest: './dist/', //<-- creates a copy
replacements: [{
from: '.js', // use string or regex to find the files you want
to: function (matchedWord) {
return matchedWord + '?<%= randomString %>';
}
}]
}
}
});
require('load-grunt-tasks')(grunt);
grunt.registerTask('default', ['replace:js']);
};
I am new to grunt and task runners in JS, so this might seem a simple question but I have been unable to find exact working answer.
I have :
concat: {
options: {
// define a string to put between each file in the concatenated output
separator: '\n\n'
},
dist: {
// the files to concatenate
src: ['scripts/app.js', 'scripts/constant.js'
],
// the location of the resulting JS file
dest: 'scripts/custom.js'
}
},
This task collects all my custom file together. What I want is to do similar thing for all my vendors file. Finally I should end up with two js only custom.js having my concatenated-minified code and vendor.js having concatenated-minfied libraries.
How do I write grunt configuration for this. Do I need to make two different tasks. If I write the above code twice with different input files, it seems to run the last code.
grunt-contrib-concat can be configured to utilize multiple-targets.
For further documentation on this subject refer to multi-tasks and Task Configuration and Targets in the grunt documentation.
Gruntfile.js
For your scenario you need to configure your concat task similar to this (Note: the new custom and vendor targets):
module.exports = function(grunt) {
grunt.initConfig({
concat: {
options: {
separator: '\n\n'
},
custom: {
src: ['scripts/app.js', 'scripts/constant.js'],
dest: 'scripts/output/custom.js'
},
vendor: {
// Modify the src and dest paths as required...
src: ['scripts/vendor/foo.js', 'scripts/vendor/baz.js'],
dest: 'scripts/output/vendor.js'
}
}
});
grunt.loadNpmTasks('grunt-contrib-concat');
grunt.registerTask('concatenate', [
'concat:custom', // <-- Targets in a task are called using a colon separator.
'concat:vendor'
]);
};
Running concat
Using the example gist provided above you can run the concat task via the CLI by typing the following command:
$ grunt concatenate
Configuring Options
If you require different configuration options for both the custom and vendor targets you will need to move the options object inside their respective targets. As explained here.
Note: Using the example gist provided the options specified will apply to both targets.
Setup:
A Gruntfile with the following task:
concat: {
build: {
files: {
'build/app.js': [
'src/.js',
'src//.js',
'!src/vendors/'
],
}
}
A lot of angular modules, with its controllers, services, and so on, with a structure like this:
a/
a.js // Module declaration like: angular.module('a',[])
a-controller.ks // Which sets a controller in its root module definition like: angular.module('a').controller()...
Issue:
The task concatenates all the js files it finds in the build folder to a single app.js file, and it does this fine, but messes up with the order of files when concatenating.
For instance, it concatenates first the controller file instead of the main folder file containing the module declaration, triggering the following error:
Module xxxx not available!
I suppose the issue lies in the way concat builds up the files and that is done by the grunt core and specifically the minimatch library, and the possibility it treats dashes to be first than letters, but I don't know how configure to change that behavior, and even know if that is possible.
Question:
So, the question is: How can I make Grunt/Grunt-concat to process dashed f first than the others in the same folder so the ordering is maintained?
Thanks
Update 1:
After digging more, It seems that it has nothing to do with the ordering inside a folder, but Grunt/Core sending the root files to the end and putting them the leaf ones first.
Just specify the order you want to concat your files, placing them in order, what I mean is, first add your single files that should be concatenated at start, after your full folder that does not need to have an order, and finally your final files, something rougth like this:
grunt.initConfig({
concat: {
js: {
src: ['lib/before.js', 'lib/*', 'lib/after.js'],
dest: 'bundle.js',
}
}
});
You will have to specify to the grunt-concat task the order you want your files built. For my projects, I typically keep a folder structure where controllers go in a app/controllers folder, services in services, and etc, but names can vary. I also keep an app.js that declares my app module and specifies the config handler for it. I use a config like this for grunt-uglify but the same can be done for concat with little to no changes:
uglify: {
development: {
files: {
'public/scripts/app.js': [
'public/app/app.js',
'public/app/controllers/*.js',
'public/app/directives/*.js',
'public/app/services/*.js'
]
}
}
}
I just copy paste my answer, the detail you want on second picture, i hope help you.
you may consider this solution
Separate the module declaration to xxx.module.js
In grunt-contrib-concat modify the config like below :
place this outside grunt.initConfig
var clientApp = './app/';
grunt-contrib-concat config
dist: {// grab module first, state the second
src: [
clientApp+'**/*-controller.js',
clientApp+'**/*.module.js',
clientApp+'**/*.state.js',
clientApp+'**/*.js'
],
dest: 'dist/<%= pkg.name %>.js'
}
i use state to so i have to define state too before trying to navigate to any state. This is preview my code, the module declaration is declared fist before anything, then my state. even minified doesnt create any problem.
I hope this help you.
i follow this johnpapa's style guide, your problem might solve there if my solution not work
I've been having an issue with grunt-usemin where it doesn't replace the non-revved reference block with the single revved line. The two files in the reference block get concatenated and uglified just fine; the single file metadata.min.js also gets versioned just fine; but, the reference to the revved file doesn't get inserted in to index.html. Just the non-revved line.
I'm using:
grunt-usemin 2.6.0
grunt-filerev 2.1.1
the Zend Framework for templating (hence the bizarre template paths)
Here's the index.html reference block before running grunt build:
<!-- build:js js/dest/metadata.min.js -->
<script src="js/metadata/MetadataController.js"></script>
<script src="js/metadata/MetadataService.js"></script>
<!-- endbuild -->
Here's the relevant Grunt config:
useminPrepare: {
html: '../cdm_common/cdm/layouts/scripts/index.html',
options: {
dest: 'dist',
root: '.'
}
},
filerev: {
options: {
encoding: 'utf8',
algorithm: 'md5',
length: 8
},
js: {
src: ['dist/js/dest/*.js'],
dest: 'js/dest/rev/test'
}
},
usemin: {
html: '../cdm_common/cdm/layouts/scripts/index.html',
options: {
assetsDirs: ['js/dest/rev/test']
}
},
grunt.registerTask('build' ['useminPrepare','concat:generated','uglify:generated','filerev','usemin']);
Here's the index.html after running grunt build:
<script src="js/dest/metadata.min.js"></script>
Any reason why the revved line shouldn't look like this?
<script src="js/dest/metadata.min.a5851d60.js"></script>
Is this a bug with grunt-usemin? Is a config off somewhere? Though not really answered, this is similar to: Usemin not replacing reference blocks in HTML
Been beating my head against the desk for awhile. Any insight is greatly appreciated.
Try running grunt --debug with the following usemin configuration for some more information:
usemin: {
html: '../cdm_common/cdm/layouts/scripts/index.html',
options: {
assetsDirs: ['js/dest/rev/test'],
blockReplacements: {
js: function (block) {
grunt.log.debug(JSON.stringify(block.dest));
grunt.log.debug(JSON.stringify(grunt.filerev.summary));
return '<script src="'+block.dest+'"></script>';
}
}
}
}
This will echo the current block its generating and an object with the files modified by filerev.
In my case I had an extra "public/" folder so my string would not match the key in the object, and therefor usemin was unable to find the new location made by filerev:
[D] "js/build/vendors.js"
[D] "public\\js\\build\\vendors.js": "public\\js\\build\\vendors.4e02ac3d2e56a0666608.js", "public\\js\\build\\main.js": "public\\js\\build\\main.acd1b38e56d54a170d6d.js"}
Eventually I fixed this with this custom blockReplacements function, (i.e. replacing public/ and the obnoxious Windows path):
js: function (block) {
var arr = {};
for (var key in grunt.filerev.summary) {
arr[key.replace(/\\/g, "/").replace(/\/\//g, "/").replace("public/", "")] = grunt.filerev.summary[key].replace(/\\/g, "/");
}
var path = (arr[block.dest] !== undefined) ? arr[block.dest] : block.dest;
return '<script src="{{ asset(\''+Twig.basePath + path +'\') }}"></script>';
},
This occurred to me as well and the issue was caused by not having the correct assetDirs in the usemin block. You will want to make sure your assetDirs array contains the parent folder of your revved file.
assetDirs documentation
This is the list of directories where we should start to look for revved version of the assets referenced in the currently looked at file.
usemin: {
html: 'build/index.html',
options: {
assetsDirs: ['foo/bar', 'bar']
}
}
Suppose in index.html you have a reference to /images/foo.png, usemin will search for the revved version of /images/foo.png, say /images/foo.12345678.png in any directories in assetsDirs options.
In others words, given the configuration above, usemin will search for the existence of one of these files:
foo/bar/images/foo.12345678.png
bar/images/foo.12345678.png
#Billy Blaze should be able to replace his custom blockReplacements function by updating the assertDirs in his usemin block to be assetsDirs: ['js/dest/rev/test', 'public']
In addition to what Micah said about getting the correct assetsDirs path, you can set the DEBUG variable to actually see what paths are being searched for your files.
If your build task is "build", then you would enter this:
DEBUG=usemin:*,revvedfinder grunt build
That will help to track down exactly what path(s) you need in assetsDirs.
I have a package.json file with our version number, such as:
{
name: "myproject"
version: "2.0"
}
My goal is to dynamically add the version number from the package.json file into the output files. For example, in the javascript I don't want to manually update the version number, but would like something similar to this to be generated after each grunt build:
/* My Project, v2.0 */
window.myProject = {
version: "2.0"
};
Is there an easy way to do this in my Gruntfile.js configuration?
I implemented: https://github.com/erickrdch/grunt-string-replace
In my source css/js files, I use the text {{ VERSION }} which gets replaced with the version number set in the package.json file. Below is the config I added to Gruntfile.js.
'string-replace': {
version: {
files: {
// the files I did string replacement on
},
options: {
replacements: [{
pattern: /{{ VERSION }}/g,
replacement: '<%= pkg.version %>'
}]
}
}
},
pkg: grunt.file.readJSON('package.json'),
I think that what you only want to do is to put some kind of trick for unable the page to use the cache files that maybe the browser have, and by now, the only way for that cross-browser is putting something on the href urls like "app.v2_2.js" or "app.js?ver=22". So I use this grunt npm package:
https://www.npmjs.org/package/grunt-cache-breaker
By default it only adds a parameter to your javascript and in almost the cases is the thing you need for not using cache, but you can configure even if you change the name of the file in other grunt process. This only change the HTML headers to what you desire.
After you install the grunt-cache-breaker, add this to your GruntFile:
// Append a timestamp to 'app.js', 'controllers.min.js' which are both located in 'index.html'
// resulting in the index the call of : href="~/app.js?rel=1415124174159"...
cachebreaker: {
dev: {
options: {
match: ['app.js', 'styles.css']
},
files: {
src: ['dist/index.html']
}
}
},
Then where you load the modules:
grunt.loadNpmTasks('grunt-cache-breaker');
Add on the task you want to:
grunt.registerTask('deploy', [
'clean:app',
'copy:views',
'copy:imgs',
'copy:css',
'uglify:app',
'cssmin:app',
'cachebreaker:dev'
]);
And finally run the grunt action on the console/command prompt
> grunt deploy
I would suggest using the banner feature in grunt-contrib-concat
this can be done as well with the banner option of https://github.com/gruntjs/grunt-contrib-uglify - which takes also care of the minifiaction of the javascript files.
filerev provides this option now. Use process to manipulate the filename that will be otherwise suffixed with md5 hash of the file content. You can use this to insert your version to every file you want.
Ref: https://github.com/yeoman/grunt-filerev
create something like package.json in the root of your project
it should read that or you can do something like
pkg: grunt.file.readJSON('package.json'),
in that you'll have a version declaration which would obviously correspond to <%= pkg.version %> so have that string in your json output and then run grunt.config.process to do the variable replacement
do something similar for the comment header