Scramblings

Dev scratchpad. Digital garden

Hugo - Copying Files to Output Using Pipes

May 27, 2024 | Reading Time: 3 min

In Hugo, copying files to the output during the build process is a common task, especially when dealing with external resources like CSS, JavaScript, or source maps from node_modules. In this post, we will walk through how to achieve this without any additional external dependencies.

Resources

  • Resources in Hugo are any files that can be processed by Hugo Pipes, such as images, stylesheets, and javascript files.
  • These resources can be transformed, minified, concatenated, and otherwise manipulated using Hugo’s built-in functions.
  • They are typically placed in the assets directory or other mounted directories.
  • A common method to copy resources involves using resource.Get followed by Permalink, RelPermalink, or Publish.
  • Here’s an example of how to copy a CSS file from node_modules to your output:
    • First, mount node_modules to the assets folder. This can be done in config.toml:

      1[module]
      2[[module.mounts]]
      3source = "node_modules"
      4target = "assets/vendor"
      
    • Note that if you do this, you have to explicitly mount other folders too. Defaults are shown in the Hugo documentation here .

    • Now you have to refer to the mount path (i.e., assets/vendor above) when using Hugo pipes to process this file. For example:

      1{{ $bootstrapCSS := resources.Get "vendor/bootstrap/dist/css/bootstrap.min.css" }}
      2<link rel="stylesheet" href="{{ $bootstrapCSS.RelPermalink }}">
      

Non-Resources

  • Non-resources are files that Hugo does not process automatically, such as certain source maps or configuration files.

  • These files might be crucial for development or debugging but are not part of the standard resource pipeline in Hugo.

  • For non-resources, the above approach doesn’t work directly because Hugo doesn’t create resources for them.

  • Instead, you can follow these steps:

    • Read the file.
    • Use resources.FromString.
    • Publish the resource.
  • To optimize this process, you can create a partial and use it in your layout.

  • Here’s an example partial that can be placed as layouts/partials/copy-sourcemap-from-nodemodules.html:

     1{{ $mapFileOrig := . }}
     2{{ $mapFileNode := printf "/node_modules/%s" $mapFileOrig }}
     3{{ $mapFileVendor := printf "vendor/%s" $mapFileOrig }}
     4{{/* warnf "Source map in: %s, node path: %s vendor path: %s" $mapFileOrig $mapFileNode $mapFileVendor */}}
     5{{ $mapContent := readFile $mapFileNode }}
     6{{ if $mapContent }}
     7    {{ $map := resources.FromString $mapFileVendor $mapContent }}
     8    {{ $map.Publish }}
     9{{ else }}
    10    {{ errorf "Source map not found: %s" $mapFileNode }}
    11{{ end }}
    
  • To use this partial, include it in your template (generally baseof.html, etc) like this:

    1{{ partial "copy-sourcemap-from-nodemodules.html" "bootstrap/dist/css/bootstrap.min.css.map" }}
    
  • Note that during development, you may need to deleted the output folder and then build your site again for resource copy to work.

Conclusion

By following these steps, you can effectively manage and include both resources and non-resources in your Hugo projects, ensuring that all necessary files are available in the final output.