I'm really late to the party, but I was also extremely frustrated by this issue and didn't find any satisfying fixes or work arounds. So I worked out some pretty dirty tricks to hopefully better work around this issue. So I'd like to share it with you.
First of all, let's quickly review why this issue happens. When usemin generates output JS/CSS files, it performs a simple path join between your dest
directory and the output directory you specified in your usemin block. So if dest
is build
and usemin block is
<!-- build:css(.) app.min.css -->
then it joins build
with app.min.css
to spit out the output file at build/app.min.css
But then the usemin task simply replaces the path in your block to you end up with
<link rel="stylesheet" href="app.min.css"/>
which is now linking the wrong directory since your HTML file is under build/dashboard/index.html
So my work around revolves around this idea: what if dest
directory is relative to where the HTML file is located? Wouldn't that solve this issue? So given the above example, what if dest
is build/dashboard
? You can see that it will spit out the output file location and link it correctly. Keep in mind that you are supposed to create a copy task to copy over your HTML files, so make sure your HTML file is copied to build/dashboard/index.html
as before.
Of course, the next question would be what if I have HTML files in multiple directories? Wouldn't that be super painful and unintuitive to create a useminPrepare target for each directory, where HTML files could reside? This is why I create a very special grunt task just for working around this issue while I was creating my own grunt scaffolding. I call it useminPreparePrepare Yes, it's deliberately named stupidly, because I'm hoping to remove this thing altogether one day when usemin people make an actual fix for this issue.
As its name suggests, this is a task to prepare useminPrepare configs. It does exactly what I described above. All of its configs mirror useminPrepare configs (in fact, most of them are simply copied over to useminPrepare), with one exception: you need to specify a src
directory to identify the root directory of all of your sources so that it can generate relative path to the HTML files. So in your example src: "."
will be fine. To use useminPreparePrepare, import it into your build first (you may want to just copy and paste my code, I don't mind), rename your useminPrepare task to useminPreparePrepare and add src
property that I just mentioned. Make sure you run useminPreparePrepare with whatever target you like, then immediately run useminPrepare without specifying target so that all of its targets are run. This is because useminPreparePrepare will generate one target for each directory relative to where HTML files are found and copies over your configs for the useminPreparePrepare target your ran. This way, your config can simply look for all HTML files.
Example
"useminPreparePrepare": {
// Search for HTML files under dashboard even though src is .
// because we want to avoid including files generated under build directory.
html: "dashboard/**/*.html",
options: {
src: ".",
dest: "build",
...
"usemin": {
html: ["build/**/*.html"],
...
"copy": {
html: {
files: [{
expand: true,
src: ["dashboard/**/*.html"],
dest: "build"
}
]
},
...
Hope this helps! Have a good day.
EDIT: I realized that given the above example, if you actually include all HTML files from current directory, you will include the generated HTML files too if they are not cleaned ahead of time. So either you clean them ahead of them or look under dashboard
directory. I'd recommend separating src
and dest
directories so that config could look a lot more intuitively.