Support multiple themes = easy plugin, dynamic merging, versioning

As users are now creating one off shortcodes, partials, js and css it would be nice if Hugo would support multiple themes.

Here is why.

I have been fiddling around with various git based tools and commands that would allow one to “merge” theme repos (layout folders) together but still maintain their separate versioning. You can merge them all right but maintaining separate versioning after the merge is impossible. You’d be able to automate the merge so you could pull new changes in. But changes to the theme that belong to the merged theme could not be pushed back (which isn’t a deal breaker). In general though this is messy and needs to be set up with some scripts cause most folks don’t know git that well.

But a simple solution is if Hugo supported multiple themes then each “component” theme could be in it’s own git submodle (subtree, subrepo) in the themes folder. I envision it working like this. In the config file the theme setting could be a singleton like it is now, but if it’s an array then Hugo would look for resources in it’s normal sequence when it goes looking at the theme if the theme parameter is an array it will look in the first theme and if it doesn’t find it in the next, etc. Thus precedence would be in order of themes in the parameter array. It would simply merge the static folders and any files therein.

For example I have a project using my theme. I want to add in @liwenyip 's gallery shortcode. I simply submodule (subtree, subrepo) it into my themes folder and in my config.toml I put

theme = ["landingpage-flex-hugo-theme","hugo-easy-gallery"] 

now I can access his shortcode in my markdown and I can add any css or js that the shortcode uses to my template(s).
If he updates his shortcode I just sync my submodule and bingo I have his updates. If I want to contribute to his project I can push my submodule to a fork and pr it back to him. In this way I can use my project for a testbed of any changes I would make to his project…sweet.

Of course it would be up to the coder not Hugo to watch for namespace collisions. I can live with that as I’d only be merging themes I knew had none or that I made sure they were overrriden correctly. It would probably be incentive for theme developers to use some of the namespace conventions like are adopted for css to avoid such issues including static filenames (i.e. use prefixes)

Famous words…but…I’m thinking it should not be a big deal to add. Hugo already walks the set theme looking for resources, just keep it walking thtrough themes before it gives up and reports an error.

I had another idea about how to make this work. I write some nodejs that merges theme folders into a third folder that the config is set to right before Hugo builds. It’s not the best solution since it requires node. Best would be to keep nodejs out of it and keep it part of Hugo :-).

Well, decided I can’t wait. For those who have node installed.

var merge = require('merge-dirs');
var watch = require('watch');

var itheme = "lp-theme"
var ithemes = ["landingpage-flex-hugo-theme", "hugo-easy-gallery"]
watchThemes(itheme, ithemes)

function watchThemes(theme, themes) {
  themes.forEach((dir) => watch.watchTree(`themes/${dir}`, (f) => {
    mergeThemes(theme, [dir])
  }))
}

function mergeThemes(theme, themes) {
  themes.forEach((dir) => merge.default(`themes/${dir}/layouts`, `themes/${theme}/layouts`, 'overwrite'));
  themes.forEach((dir) => merge.default(`themes/${dir}/static`, `themes/${theme}/static`, 'overwrite'));
  console.log(`themes ${themes} merged into ${theme}`)
}