Support for HTML/CSS/JS preprocessors

thread copied from Support for preprocessors · Issue #16 · gohugoio/hugo · GitHub

redalastor commented on Aug 6, 2013
Thinking about the issue a bit, I found the following things that would be a must to be able to specify in the config:

  • pattern of the preprocessed files
  • command to preprocess everything
  • command to preprocess a single file
  • command to start a watch

Not all of those would be required. If there is no command to preprocess everything then every file is converted individually. If there’s a watch mechanism, then hugo doesn’t have watch for changes in those files by itself.

2 Likes

realchaseadmas commented on Sep 14, 2014
This seems like it’s asking a lot of Hugo, as Hugo’s responsibility is static site generation, not asset processing. You can easily use a build tool such as Make, gulp, grunt, golang’s train and watch these files to the /static directory, (from a /src directory), where those files are already being copied to /public.

Hugo, as far as I know, is unaware of the actual assets in the /static directory, just that the files are in the directory to copy to the /public directory, so configuration for a non-standard format seems like a lot of extra cruft built into a tool that’s not meant for that.

adrinux commented on Sep 16, 2014
That seems an odd distinction @realchaseadams – is Hugo not, essentially, an HTML preprocessor? Can you seriously argue that CSS isn’t a part of a static web site?

I was pondering SASS preprocessing since I’m using that to build a site, currently my scss files are in amongst the other files making the site which results in Hugo watch rebuilding once when I save the .scss files and once when compass watch has processed them into .css files in themes/mytheme/static/css – no big deal because it’s so fast, but still – so what would best practice be? Keep scss/js etc entirely outside of the site folder? Does Hugo ignore /src?

There’s some attraction to letting 3rd party tools do the work they’ve been designed for, but I’m still having difficulty figuring out the best workflow, filesystem layout etc.

So incorporating a wrapper for something like libsass is out of the question for Hugo?

realchaseadams commented on Sep 16, 2014
I wouldn’t do anything seriously (sorry if I came across as suggesting there was any real gravity), but I would genuinely argue that a pre-processed asset doesn’t belong in the /static directory (because it’s not static, it’s dynamic), nor should it be processed by, a static website generator.

Hugo won’t watch a directory it’s not told to watch, and in the case of any pre-processed asset, I’d argue that it deserves to be in a directory outside of the /static directory.

Architecturally, I personally have a /src directory with sass & js files, because I’m ultimately going to want to process and mutate them in a production build, minified, with asset references revved, and not “put” the original SASS or CoffeeScript or LESS files into the public directory, since they don’t have a purpose there.

I will run hugo server --watch and my default gulp task, which watched the /src directory for changes, processes assets and writes the new files into the /static directory, which hugo copies into the /public directory. This process works really well for me, and I find that my workflow is better off for it, as well as keeping your statically built assets out of version control if you add /static/css and /static/js to your .gitignore (which I do because I believe built/compiled files don’t belong in version control).

When you consider trying to ask hugo to do something like this, there are a lot of architectural decisions you’d have to make:

  • Is sass going to be a flag?
  • If you incorporate it, is there a schema for where the sass files should live, or should you let the user define it in the config?
  • If you’re going to have sass as an option, why not offer less, compass, coffeescript, all the other pre-processors that are involved in a workflow?

You made a great point about Hugo being an HTML pre-processor, but I think it speaks more to the fact that Hugo should just be that, an HTML preprocessor that copies static assets into an output directory. The advantage of Hugo is that it’s really fast at compiling html assets and copying. Aside from the ease of not having to run sass in a separate shell, what advantage would one gain from having Hugo run a shell command?

I just contribute to the conversation and the cod sometimes, so ultimately the decision isn’t up to me, and this is my two cents, which in any economy is pretty worthless, so the decision to incorporate a wrapper for something to compile your sass for you isn’t up to me. :slight_smile:

1 Like

isaaki commented on Sep 26, 2014
Interestingly, turns out there a css pre-processor written in Go called GCSS: GitHub - yosssi/gcss: Pure Go CSS Preprocessor

If some type of css pre-processor support was to be incorporated into Hugo but without support for all of them, perhaps this would be a good one to go with (for no other reason than it being written in Go, for whatever that might be worth). It could be approached in a “zero config” spirit similar to the idea of Harp.

Just a thought related to the topic of Hugo and css preprocessors.

I think everybody coming to Hugo with an existing workflow has a kneejerk response of expecting Hugo to takeover everything – I know I did. But there’s something to be said for using existing well developed tools alongside hugo (grunt/gulp/compass etc).

The one caveat is Hugo’s selling point of being a single binary install – if you still have to get in place an entire stack of ruby and node.js modules to actually build the site that advantage is kind of moot :slight_smile:

That said there are clear moves to improve at least the SASS situation outside of Hugo, like libsass (go wrappers around that have been written too.).

I’m also coming to the conclusion that image processing and optimization should be done prior to hitting Hugo, they tend to be one off or infrequent processes and having them under the umbrella of hugo is unnecessary.

2 Likes

I’ve been a happy Hugo-user for a couple of months, and have been doin’ some thinkin’

I think most of this static resource processing can be done outside of Hugo, but Hugo need some kind of hook/plugin mechanism.

So to bullet list it:

  • I would be happy to recommend Hugo as an alternative to WP or other to my customers for certain kind of use cases.
  • They would want to do some “company specific mods” (i.e. mods not really PR worthy), but they will be very reluctant about creating their own fork (they will have a fork, but it will stay very close to the original)
  • No problem about building from source, but getting new versions should be as easy as “git pull”
  • So Go’s statically linking creates some challenges, not possible to add libs runtime
  • But it should be possible to link to a set of well defined (implements a Hugo interface) custom plugins and pull them in during compile time.

This isn’t all thought out from my side – but I believe Hugo will need something like this at some point to be really successful.

Has there been any changes to support the hooks @bjornerik suggested or is the current situation still basically DYI for preprocessors?

I am new to Hugo so if anyone can suggest a workflow for integrating preprocessor(s) it would be much appreciated.

EDIT: realchaseadams’ suggested workflow is pretty nice and I think will get me started for now. If anyone has any other workflow that works for them it would be great to hear about.

1 Like

No major progress on that front (but a lot of stuff happened on other fronts).

I use Gulp in combo with Hugo and it works great:

1 Like

Wrote-up a post on using NPM as a build tool with Hugo… super simple and works well for my general use cases:

http://thecodestead.com/post/how-to-use-npm-as-a-build-tool-with-hugo/

Essentially, this:

"scripts": {
    "css:build": "scss-cli --source-map --output-style compressed './static-src/src-assets/sass/**/*.{scss,sass}' --glob -o static/assets/css",
    "postcss:build": "autoprefixer -b 'last 2 versions' static/src-assets/css/*.css",
    "css:watch": "onchange './static-src/assets/src-sass/' -- npm run css:build",
    "js:build": "browserify static-src/assets/js/index.js -o static/assets/js/bundle.js",
    "js:watch": "watchify static-src/assets/src-js/index.js -o static/assets/js/bundle.js",
    "build": "npm run css:build && npm run js:build",
    "prewatch": "npm run build",
    "watch": "parallelshell 'npm run css:watch' 'npm run js:watch'",
    "start": "npm run watch",
},

Read the post for explanations and such.

2 Likes

+1 Bring your own build system.

Example Gruntfile adapted from a blog post:

module.exports = function(grunt) {
  'use strict';

  grunt.initConfig({
    pkg: grunt.file.readJSON('package.json'),

    shell: {
      hugo: {
        command: function(target) {
          if (target === 'dev') {
            return 'hugo --baseUrl=http://127.0.0.1 --buildDrafts=true --buildFuture=true --source=site --destination=../build/dev';
          } else {
            return 'hugo --source=site --destination=../build/dist';
          }
        }
      }
    },

    connect: {
      dev: {
        options: {
          hostname: '127.0.0.1',
          port: '80',
          protocol: 'http',
          base: 'build/dev',
          livereload: true
        }
      }
    },

    sass: {
      dev: {
        options: {
          sourceMap: true
        },
        files: {
          'css/styles.css': 'scss/styles.scss'
        }
      },
      dist: {
        options: {
          outputStyle: 'compressed'
        },
        files: '<% sass.dev.files %>'
      }
    },

    jshint: {
      files: ['Gruntfile.js', 'js/**/*.js', '!js/vendor/*.js'],
      options: {
        reporter: require('jshint-stylish'),
        force: true,
        globals: {
          jQuery: true
        }
      }
    },

    watch: {
      options: {
        livereload: true
      },
      site: {
        files: ['site/**/*'],
        tasks: ['shell:hugo:dev']
      },
      js: {
        files: ['<%= jshint.files %>'],
        tasks: ['newer:jshint', 'shell:hugo:dev']
      },
      sass: {
        files: ['scss/**/*.scss'],
        tasks: ['sass:dev', 'shell:hugo:dev']
      }
    }
  });

  require('load-grunt-tasks')(grunt);

  grunt.registerTask('default', ['connect:dev', 'jshint', 'sass:dev', 'shell:hugo:dev', 'watch']);
  grunt.registerTask('build', ['jshint', 'sass:dist', 'shell:hugo']);

};
1 Like

Hi, I created a Hugo + Gulp starter project (hugulp), which is similar to the asset pipeline in Ruby Rails.

It minifies css/javascript (with scss preprocessing) and compresses images, before fingerprinting each resource.

There’s an article with more details here.

Hugulp shows up about in the middle.

3 Likes

I maintain a Go wrapper for libsass at: https://github.com/wellington/wellington. Rather than creating a port of Sass to Go, this leverages the work of libsass which will be 100% compatible with the Sass specification. Wellington builds with the standard Go toolchain. If there’s some interest in integrating these, I’ll take a look at the hugo project and see what is needed.

I really like Hugo and it would be awesome to use Sass with it!

4 Likes

@drewwells This looks pretty neat. I’d definitely be interested in exploring integration here.

3 Likes

I’d highly recommend using Brunch as your build system. It mimics the Rails asset pipeline, supports all the standard preprocessors, and compiles to the public directory by default. Follow the instructions in the guide inside your Hugo site and you’ll be up and running in no time.

Since I just got started with Hugo, I didn’t realize that static is the intended location for static assets. You’ll want to add the following to your brunch-config.coffee:

module.exports = config:
  paths:
    public: 'static'

Now, run hugo server --watch in one terminal session, and brunch w in another. Whenever you make a change to your Sass files, for example, they’ll be automatically compiled to static, and Hugo will reload the browser. Nifty!

@jbrodriguez

I tried this (Windows 7 x64) but could not get gulp to complete the build script.

It fails when the hugo command is run.

What makes it super confusing (I’m also new to gulp) is that the hugo project is called hugo (so it’s hard to know what refers to hugo and what to the project).

Also I don’t know if this is a gulp thing, but there are layers of dependency which make modifying it hard. If this is how gulp works, its not for me. Example:

reference:all links to hugo:all and build:all looks for reference:all. I tried to remove the hugo part of the gulp process but it got messy.

Happy to look more into this to get it working and with feedback for others.

Hi @mikeaja, what error message are you getting ?

Could you open an issue on github, with some more details to try and figure it out ?

Error is ‘Command failed’

It doesn’t seem to matter what thhe hugo command is, the error is the same.

I also tried running gulp both in the main hugo folder (where all hugo sites are and where gulp is installed) and also with the project itself. The result is the same.

Could it be that the gulp command is trying to run hugo in the parent (main hugo folder) rather than the project folder?

Still, I’d be happier anyway jjust using the gulp side to do the pre–hugo processing, and running hugo afterwards.

Bit late to this party but I’ve recently switched from using Textmate to Atom and, one of the packages I’ve installed in Atom is Less-Autocompile, which automatically pre-processes .less files into their corresponding .css file. All you need to do is add a couple of lines of comment at the top of the .less file to tell Less-Autocompile where to output the compiled .css file and it will automatically generate it, every time you hit save.

I’d be a bit wary of over-burdening Hugo itself with too much ‘everything but the kitchen sink’ functionality [Although a plugin system might mitigate this a bit].

Anyway, having previously abandoned attempts at working with CSS pre-processors because of the lack of integration with my [then] workflow, I’m pretty impressed with how seamless my newly discovered Atom + Less-Autocompile makes it.

Shameless pluggage:

I’ve written about this and a bunch of other packages I find useful in Atom here:

My Atom Setup

Given that probably 90%+ of the time I spend in Atom is working on my Hugo sites [whether writing posts, hacking templates or building themes], it may be of interest to others of you looking for a near-as-dammit “one stop shop” for your Hugo tinkering.