grunt-usemin block paths not relative to html file?
Asked Answered
F

2

6

Having a lot of trouble understand how paths are treated at various points in the configuration and usage of grunt-usemin.

I've got the following repo layout, where the repo root will also be the web app root:

/dashboard/index.html
/Gruntfile.js
/vendor/...some 3rd party CSS and JS...

So the index.html file -> somedomain.com/dashboard/index.html.

The index.html file includes some CSS and JS assets from the /vendor folder. I have grunt configured to put build output in a build folder:

/build/dashboard/index.html

In the index.html file, I have usemin blocks wrapped around all the CSS link and JS script tags:

<!-- build:css(.) app.min.css -->
<!-- build:js(.) app.min.js -->

I had to specify an "alternative search path" with "(.)" so that a script tag for "/vendor/backbone.js" will find it in the right place. Until I did that, it was looking for /dashboard/vendor/backbone.js.

I want the output of processing the CSS/JS assets to be output to build/dashboard/app.min.css and build/dashboard/app.min.js, and included by index.html using a simple relative "app.min.css/js" path.

The problem is, grunt-usemin seems to be using the "app.min.*" path I'm specifying for both contexts in a way that makes it impossible for them to work together:

1) It treats the path as relative to the build directory for purposes of creating the file; the files end up in build/app.min.css and build/app.min.js.

2) It treats the path as relative to the index.html file for purposes of generating the new link/script tags; the browser loads build/dashboard/index.html, which then tries to load "app.min.css", which maps to build/dashboard/app.min.css.

Is there a solution?

Foxglove answered 25/9, 2013 at 20:48 Comment(0)
A
3

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.

Amye answered 8/2, 2014 at 19:51 Comment(0)
F
2

I don't like it, but the only way I've found to make it work so far is to specify a full path:

<!-- build:css(.) /dashboard/app.min.css -->
<!-- build:js(.) /dashboard/app.min.js -->

The leads to the app* files being in /build/dashboard alongside index.html (which is where I want them), and index.html ends up with the following tags:

<link rel="stylesheet" href="/dashboard/app.min.css">
<script src="/dashboard/app.min.js"></script>

It means the dashboard app is now acutely aware of it's location within the whole, so you can't just rename or relocate it's position in the tree without updating those paths.

Foxglove answered 25/9, 2013 at 20:56 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.