yeoman: how to compress components? - gruntjs

In yeoman application all my scripts are minified and compressed into single file, but all the "components" are copied as is which is an absurd - because it's a ~130 files in the starter project...
This what I belive is responsible for the concatenation task and the question is how to make it include my components which are mentioned in the index.html file? What parameters are to the files object are? Can't find anything in the documentation.
concat: {
dist: {
files: {
'<%= yeoman.dist %>/scripts/scripts.js': [
'.tmp/scripts/{,*/}*.js',
'<%= yeoman.app %>/scripts/{,*/}*.js'
]
}
}
},

I think what you're looking for is grunt-usemin.
When you include your components, you can wrap your <script> tags in <!-- build:js js/foo.js -->.
<!-- build:js js/main.js -->
<script src="js/carousel.js"></script>
<script src="js/index.js"></script>
<!-- endbuild -->
The useminPrepare task that's included in the package will cycle through any scripts within that block and add them to the concat/uglify task. Your task list might then include something like this:
useminPrepare: {
html: [ '<%= yeoman.dist%>.html' ],
options: {
uglify: 'uglify'
}
},
usemin: {
html: [ '<%= yeoman.dist%>.html' ],
options: {
basedir: 'dist'
}
},
Output to your foo.html would then be whatever you specify in the build comment. In my case, the scripts are concatenated, minified, and the reference to them is replaced with <script src="js/main.js"></script>.

Related

cant replace HTML base tag with grunt-usemin's custom blockReplacements

I'm trying to replace the HTML base tag's href attribute value with /mydir/. Have attempted this with grunt-usemin's blockReplacements.
Here is the configuration
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
useminPrepare: {
html: ['<%= yeoman.client %>/index.html'],
options: {
dest: '<%= yeoman.dist %>/public'
}
},
// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
html: ['<%= yeoman.dist %>/public/{,*/}*.html'],
css: ['<%= yeoman.dist %>/public/{,*/}*.css'],
js: ['<%= yeoman.dist %>/public/{,*/}*.js'],
options: {
assetsDirs: [
'<%= yeoman.dist %>/public',
'<%= yeoman.dist %>/public/assets/images'
],
blockReplacements: {
baseUrl: function (block) {
grunt.log.debug("******************* blockReplacements *******************");
return '<base href="/mydir/">';
}
},
// This is so we update image references in our ng-templates
patterns: {
js: [
[/(assets\/images\/.*?\.(?:gif|jpeg|jpg|png|webp|svg))/gm, 'Update the JS to reference our revved images']
]
}
}
}
My html that is calling usemin:
<!-- build:baseUrl /mydir/ -->
<base href="/">
<!-- endbuild -->
I am running:
$ grunt serve:dist
But this only removed the base tag and does not replace.
I have a suspicion I need to configure a baseUrl attribute within the Gruntfile, but I'm not trying to rev/uglify files and believe this attribute would point files that need reving etc.
Also I've tried running debug to fire grunt.log.debug calls the buildReplace function:
$ grunt --debug serve:dist
But there is no console output.
Note: My application is based on angular fullstack generator application.
https://github.com/angular-fullstack/generator-angular-fullstack
Any help or suggestions appreciated.
You must use this command:
grunt build
After getting no where, I have not been back to this issue for a while. Having just revisited, it seems upgrading from grunt-usemin": "~2.1.1" to grunt-usemin": "3.0.0" fixed the issue. Still not sure if a specific bug was address to fix this in 3.0.0
Also Note: baseUrl in the Gruntfile is assigned a function with first parameter being block:
baseUrl: function (block) {
grunt.log.debug("******************* blockReplacements *******************");
return '<base href="/mydir/">';
}
This parameter named block is not actually used in the function, the href return is a string (I know this is bad and shouldn't be used this way, but just pointing something out here). The call to this function in client/index.html must have a parameter passed to it:
<!-- build:baseUrl /mydir/ -->
<base href="/">
<!-- endbuild -->
If you take away /mydir/ url portion the following would fail to create the base tag:
<!-- build:baseUrl -->
<base href="/">
<!-- endbuild -->
So in effect we could add /some-rubbish-url-portion/ and it would not fail and would not have any effect on the base tag's outcome.

Grunt dynamic dest location sass

This could possibly be a repeat question but I couldn't figure out a solution for my requirement
I am trying to create a sass grunt task which can generate css files in a dynamic location. Here is my structure
/components
--> xyz
--> scss
--> xyz.a.scss
--> xyz.b.scss
--> abc
--> scss
--> abc.a.scss
--> abc.b.scss
Can the grunt task create a new folder relative to its component i.e
/components
--> xyz
--> scss
--> xyz.a.scss
--> xyz.b.scss
--> css
--> xyz.a.css
--> xyz.b.css
--> abc
--> scss
--> abc.a.scss
--> abc.b.scss
--> css
--> abc.a.css
--> abc.b.css
My current SASS task, generates CSS files in the same folder of SCSS
sass: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.client %>/components/',
src: ['**/*.scss'],
dest: '<%= yeoman.client %>/components/',
extDot: 'last',
ext: '.css'
}]
}
},
I understand we could achieve this by providing component folder name in the dest, for example for xyz component I could use dest as <%= yeoman.client %>/components/xyz/css. But I will have to write seperate task for each component. Is there a way to keep dest in the same parent folder without actually specifying the name of the folder? i.e src: ['**/scss/*.scss'] and dest be: ['**/css/'] Is there a way to do this?
You can use dynamic parameters to pass in the task:
for example:
grunt sass:xyz
than in the code you can use something like this:
sass: {
dist: {
files: [{
expand: true,
cwd: '<%= yeoman.client %>/components/',
src: ['**/<%= grunt.task.current.args[0] %>/sass/*.scss'],
dest: '<%= yeoman.client %>/components/<%= grunt.task.current.args[0] %>/css',
extDot: 'last',
ext: '.css'
}]
}
},
the only things that you must set is a generic task that execute it for all the components for example:
grunt.registerTask('sassAll', [ 'sass:xyz', 'sass:abc' ]);
you can even generate an array and cycling it:
grunt.registerTask('sassAll', function() {
gruntUtils.sassTasks.forEach(function(param){
grunt.task.run('sass:' + param);
});
});
var gruntUtils = {
sassTasks : [ 'xyz', 'abc' ]
};
you can use even more parameters for setting dest and source:
grunt sass:xyz/sass:xyz/css
and reference to the second parameter:
<%= grunt.task.current.args[1] %>
I found a way to retrive the directory(not sub-directory) inside a base one:
var templates = grunt.file.expand({
filter: "isDirectory",
cwd: "(your base dir path in relation to the gruntfile.js)/components"
},["*"]);
var dirs = templates.map(function (t) {
return t;
});
than you have an array(dirs) to use instead of [ 'xyz', 'abc' ]

Grunt usemin task not updating nested relative paths correctly

I have a Yeoman project scaffolded with 'webapp-generator' which contains a static website with nested HTML files, resembling this structure:
-root/
index.html
-directory/
file1.html
-directory2/
file2.html
-js/
(revved .js files)
-css
(revved .css files)
I'm using usemin and filerev tasks to update the filerevved file paths on the .html documents. It updates all file names correctly on js/css/images and it works correctly on the root index.html.
However, on the nested HTML files, it doesn't replace the reference to the correct nested path.
For example.
js/scripts.js gets renamed to js/827385.scripts.js
In index.html
<scripts src="js/scripts.js"></scripts>
resolves to: <scripts src="js/827385.scripts.js"></scripts>
But in directory/file1.html (or any other nested html file)
<scripts src="../js/scripts.js"></scripts>
gets also converted to: <scripts src="js/827385.scripts.js"></scripts>
Ignoring the ../ relative path
Is there any way to tweak the Grunt tasks to be aware of the relative depth of the file within the directories to keep the relative indicator ../ in the renamed path?.
Below is the code snippet for the relevant Grunt tasks.
PS: I have already followed some of the possible answers in this Stack Overflow question: How should I configure grunt-usemin to work with relative path to no avail.
// Renames files for browser caching purposes
rev: {
dist: {
files: {
src: [
'<%= config.dist %>/scripts/{,**/}*.js',
'<%= config.dist %>/styles/{,**/}*.css',
'<%= config.dist %>/images/{,**/}*.*',
'<%= config.dist %>/styles/fonts/{,**/}*.*',
'<%= config.dist %>/*.{ico,png}'
]
}
}
},
// Reads HTML for usemin blocks to enable smart builds that automatically
// concat, minify and revision files. Creates configurations in memory so
// additional tasks can operate on them
// <%= config.dist %>
useminPrepare: {
options: {
dest: 'out',
// root: '<%= config.app %>'
},
html: '<%= config.app %>/index.html'
// root: '<%= config.app %>'
},
// Performs rewrites based on rev and the useminPrepare configuration
usemin: {
options: {
assetsDirs: [
'<%= config.dist %>',
'<%= config.dist %>/images',
'<%= config.dist %>/styles'
]
},
html: ['<%= config.dist %>/**/*.html'],
css: ['<%= config.dist %>/styles/**/*.css']
},
In my case I've found out that the problem was usemin configuration in the html files:
If you have in your index.html this usemin directive:
<!-- build:js styles/jquery.js -->
<script src="bower_components/jquery/dist/jquery.js"></script>
<!-- endbuild -->
In directory/file1.html you should try to use a full path in it:
<!-- build:js /scripts/jquery.js-->
<script src="../bower_components/jquery/dist/jquery.js"></script>
<!-- endbuild-->
which will be converted to something like <script src="/scripts/jquery.34ad31c3.js"></script>.

Grunt usemin not replacing file references in sub directories or in compiled CSS file

I'm finding the grunt-usemin tasks a little confusing to set up. I'm using grunt-usemin to concatenate, uglify and rev my files. It is working for the root index.html file, however, files in other directories are not getting replaced with the revved file reference.
I have the following file structure:
app
index.html
subfolderA
--index.html
subfolderB
--index.html
assets
--img
--js
----script.js
--scss
----style.scss
On build, everything gets built into /dist directory.
My Gruntfile contains (among other tasks) the following:
useminPrepare: {
options: {
dest: '<%= yeoman.dist %>'
},
html: '<%= yeoman.dist %>/**.html',
css: '<%= yeoman.app %>/assets/scss/**.scss'
}
usemin: {
options: {
dirs: ['<%= yeoman.dist %>']
},
html: '<%= yeoman.dist %>/**.html',
css: '<%= yeoman.app %>/assets/css/style.css'
}
grunt.registerTask('build', [
'clean:dist',
'jekyll:dist',
'spriteHD',
'compassMultiple:dist',
'useminPrepare',
'concat',
'uglify',
'rev',
'copy:dist',
'svgmin',
'imagemin:dist',
'svg2png',
'modernizr',
'concurrent:replacementsDist',
'usemin'
]);
Blocks to be replaced in the HTML files look like:
<!-- build:js /assets/js/lteie8.main.js -->
<script src="//ajax.googleapis.com/ajax/libs/mootools/1.4.5/mootools-yui-compressed.js"></script>
<script src="/assets/bower_components/selectivizr/selectivizr.js"></script>
<script src="/assets/bower_components/respond/dest/respond.src.js"></script>
<!-- endbuild -->
Once built, dist/index.html shows all the correctly revved file names - for css, js concat blocks and all images. However, the compiled CSS file does not contain the revved file references. Nor in all subdirectory index.html files do the file references get updated to the revved versions.
With me referencing the HTML files with **.html I thought that should target all html files in subdirectories. I presume I have some mistake in the usemin or useminPrepare task, is this the case?
== EDIT ==
In response to przno's answer I changed usemin:css: to <%= yeoman.dist %>/assets/css/*.css to get it to compile the CSS correctly.
With me referencing the HTML files with **.html I thought that should target all html files in subdirectories - use **/*.html

How should I configure grunt-usemin to work with relative path

I have a grunt project backed by a yeoman-generator that I've built based on the generator-webapp, if it's of any help, you can find it on GitHub
The grunt project makes us of the grunt-usemin task.
My project involve building a multilingual website, and to keep things clean, I've decided to put all the pages written in a language in a folder name after the 2-letter shortcode of the said language.
| project/
|--dist/
|----en/
|------index.html
|------404.html
|------...
|----fr/
|------index.html
|------404.html
|------...
The files are made from handlebars templates and processed with assemble. In the layout I have building blocks for usemin such as
<!-- build:css(.tmp) styles/main.css -->
<link rel="stylesheet" href="../styles/main.css">
<!-- endbuild -->
<!-- build:js scripts/vendor/modernizr.js -->
<script src="../bower_components/modernizr/modernizr.js"></script>
<!-- endbuild -->
Which, in a perfect world would translate to
<link rel="stylesheet" href="../styles/main.css">
<script src="../scripts/vendor/modernizr.js"></script>
but instead shows
<link rel="stylesheet" href="styles/main.css">
<script src="scripts/vendor/modernizr.js"></script>
which is less than ideal in my case.
The relevant part of the Gruntfile.js looks like this
useminPrepare: {
options: {
dest: '<%= yeoman.dist %>'
},
html: [
'<%= yeoman.app %>/fr/{,*/}*.html',
'<%= yeoman.app %>/en/{,*/}*.html'
]
},
usemin: {
options: {
dirs: ['<%= yeoman.dist %>']
},
html: [
'<%= yeoman.dist %>/fr/{,*/}*.html',
'<%= yeoman.dist %>/en/{,*/}*.html'
],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
}
I have tried to use the basedir option
by setting it to <%= yeoman.dist %> as well as changing the build blocks to
<!-- build:css(.tmp) ../styles/main.css -->
<link rel="stylesheet" href="../styles/main.css">
<!-- endbuild -->
<!-- build:js ../scripts/vendor/modernizr.js -->
<script src="../bower_components/modernizr/modernizr.js"></script>
<!-- endbuild -->
But unfortunately wasn't able to get a proper output.
More specifically, the first one didn't change anything, the second one had the folders scripts and styles outputted one level too high in the hierarchy
| project/
|--app/
|--dist/
|--styles/
|--scripts/
instead of
| project/
|--app/
|--dist/
|----styles/
|----scripts/
Would anyone happen to know what to do ? It seems a rather simple usecase but I couldn't find the help I need via Google, GitHub or SO...
I believe that you can achieve what you need in this way:
Html file:
<!-- build:css styles/main.css -->
<link href='../styles/css/style.css' rel='stylesheet' type='text/css'>
<link href='../styles/css/responsive.css' rel='stylesheet' type='text/css'>
<link href="../styles/css/skins/welld.css" rel='stylesheet' type='text/css' id="skin-file">
<!-- endbuild -->
Gruntfile.js
useminPrepare: {
options: {
dest: '<%= yeoman.dist %>/'
},
html: ['<%= yeoman.app %>/snippets/head.html','<%= yeoman.app %>/snippets/tail.html']
},
usemin: {
options: {
dirs: ['<%= yeoman.dist %>/'],
blockReplacements: {
css: function (block) {
return '<link rel="stylesheet" href="../' + block.dest + '"/>';
},
js: function (block) {
return '<script src="../' + block.dest + '"></script>';
}
}
},
html: ['<%= yeoman.dist %>/{,*/}*.html'],
css: ['<%= yeoman.dist %>/styles/{,*/}*.css']
}
The key solution is in the blockReplacements option of the usemin task. Basically, the task will put your files under <%= yeoman.dist %>/styles/main.css, while your html will be under <%= yeoman.dist %>/en/somefileinEnglish.html and every instance of 'styles/main.css' in this file will be replaced with '../styles/main.css', adding the correct relative path.
As an extra tip, if you are building a multilingual website, you may want to consider grunt-i18n to translate your file while building, so you won't need to maintain a different html file for every language.
I was building my own Grunt scaffolding and I was frustrated by this issue. I managed to create a work around that I'd like to share it with you.
I'd like to use an example of my own to illustrate the reason behind his issue and how I managed to work around. Say my directory structure is the following:
| in/
+---- pages/
| +---- login.html
+---- js/
| +---- s1.js
| +---- s2.js
+---- index.html
| out/
| Gruntfile.js
Where in/ is where all of my source files reside and out/ is dest directory, where all of the output files will be stored. Say I want to import s1.js into login.html, I'd write something like this:
<!-- build:js ../js/login.js -->
<script src="../js/s1.js"></script>
<!-- endbuild -->
In here, usemin block performs a simple string replace so the output path should be exactly where I want to link the output file. The problem occurs when instead of having login.js land at out/js/login.js, useminPrepare end up landing it at js/login.js. The reason behind this is that useminPrepare simply performs a path join between dest (which is out) and the output path (which is ../js/login.js), while it should have performed a path join with respect to where the HTML file is found.
In order to work around this issue, observe that if I set dest to out/pages which respects where login.html is found, it will work out fine. BUT notice that if index.html imports js/s2.js in a similar fashion, then that will get screwed up. So in order to work around THAT, we need to create one useminPrepare target for index.html with dest: "out" and another target for login.html with dest: "out/pages". Hence my useminPrepare config now looks something like this:
"useminPrepare": {
t1: {
dest: "out",
files: [{src: ["in/*.html"]}],
...
},
t2: {
dest: "out/pages",
files: [{src: ["in/pages/*.html"]],
....
}
}
All targets will have to run. Now you will probably say; what if I have even more HTML files under other subdirectories? Does that mean I will have to create one target for each directory where HTML files are found? This is pain in the ass and stupid. It IS! I agree. So I wrote a very special grunt task to help out. I call it useminPreparePrepare I deliberately named it stupidly, because it IS stupid. I'm really hoping to get rid of this work around one day when usemin people fixes this issue. As its name suggests, useminPreparePrepare prepares configs for useminPrepare. Its own configs mirrors useminPrepare (in fact, most configs are simply copied over) with one exception; you will have to add a src property that points to the source root directory where all of your source files reside, so that it can figure out the relative path between your source root and HTML files. It will perform essentially what I mentioned above. Plus, it does the same for staging directory too, so staging files wont' break out of the staging directory. You can even have multiple targets in useminPreparePrepare, it will copy over the options for the target you ran.
In order to use this work around, you will first have to import useminPreparePrepare. I didn't put it on npm, so you will have to just copy and paste it. I don't mind. Then simply rename your useminPrepare config to useminPreparePrepare, and add src property. For the above example, src: "in". Then you need to run useminPreparePrepare with whichever target you'd normally like, then immediately run useminPrepare without specifying target so that all targets will run. Given the above example Grunt config could look something like this:
"useminPreparePrepare": {
html: "in/**/*.html", // All HTML files under in/ and its subdirectories.
options: {
src: "in",
dest: "out",
....
}
},
"copy": {
html: { // Copies all HTML files from in/ to out/ preserving their relative paths.
files: [{
expand: true,
cwd: "in",
src: ["**/*.html"],
dest: "out"
}
]
}
},
"usemin": {
html: "out/**/*.html", // Process all HTML files under out/ and its subdirectories.
...
}
You can see that the above config is simple enough to include all HTML files recursively under the source directory. useminPreparePrepare takes care of all the stupid work arounds while looking just like useminPrepare.
I hope this helps!
My solution was to manually modify grunt-usemin plugin.
Open node_modules/grunt-usemin/fileprocessor.js in you favourite text editor. Somewhere around line 152 find:
if (assetSearchPath && assetSearchPath.length !== 0) {
this.file.searchPath = assetSearchPath;
}
and replace it with:
if (assetSearchPath && assetSearchPath.length !== 0) {
this.file.searchPath.splice(0, 0, assetSearchPath);
}
By default this.file.searchPath is an array containing the absolute path to the current file. There's no point in overwriting it with ['dist'], it's better to prepend the array with 'dist'. This way if an item is not found in 'dist' directory it might be concluded that either the path is relative or it's wrong. Thus we use the second value from the searchPath array to look for the file and if it was a relative path - we get what we wanted.
I used multiple targets as shown below:
useminPrepare: {
target1 : {
src : ['target1/*.html'],
options : {
dest: 'out'
}
},
target2 : {
src : ['target2/folder/*.html'],
options : {
dest: 'out/subfolder'
}
}
},
Then in the HTML block you could this:
<!-- build:js ../subfolder/script.js -->
<script src="../subfolder/scripts/main.js></script>
<!-- endbuild -->
I have seen many questions about this and no real answers. In my project I have mapped the "dist" folder to "/static" server side. So I don't need to figure the relative path in index.html.
But still, the issue remains with usemin
<!-- build:css(.tmp) static/css/main.css -->
<link rel="stylesheet" href="css/main.css">
<!-- endbuild -->
the usemin file will be written in dist/static/css/main.css
and the the HTML will show the wrong (but expected) path
<link rel="stylesheet" href="static/css/main.css">
The only workaround I have found is to not touch the usemin block, run grunt and update the path manually.
If I'm understanding your question correctly I think you need the 'root' option:
{
useminPrepare: {
html: 'html/index.html',
options: {
root: 'app'
dest: 'dist'
}
}
}
Though index.html is in the html/ folder the files lookup will be from app/ not html/
I make the usemin blocks relative to the template.
I have a structure like this:
app/ - webroot source (devmode)
app/views/layouts - layouts for server-generated pages (also has usermin tags init)
app/js - source javascript
dist/ - production build dir
I make my html look like this in app/layouts/main.html:
<!-- build:js js/static.min.js -->
<script src="../../js/controllers/StaticCtrl.js"></script>
<script src="../../js/directives/backImg.js"></script>
<script src="../../js/smooth-scroll.js"></script>
<!-- endbuild -->
on dev (no usemin, just serving files) "../../" cleverly just resolves to "/", so it still works. This way you don't need to preprocess while you are developing (with watch tasks or whatever.)
useminPrepare: {
loyalty: {
src: '<%= loyalty.app %>/Views/index.tpl',
options: {
dest: '.',
flow: {
html: {
steps: {
js: ['concat', 'uglifyjs'],
css: ['cssmin']
},
post: {}
}
}
}
}
},
<!-- build:css /resources/v4/css/loyalty/index.css -->
<link rel="stylesheet" href="../Resources/css/main.css">
<link rel="stylesheet" href="../Resources/css/product.css">
<link rel="stylesheet" href="../Resources/css/use_code.css">
<!-- endbuild -->
use '.' as destination in gruntfile.
I make a bug fix for the relative path.
https://www.npmjs.com/package/grunt-usemin-fix
You can copy and past the change to source of use-min to use the relative path

Resources