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 mak
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.
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!
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
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 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.