Search code examples
handlebars.jsyamltemplatingassemble

Lodash Templates/Variables not processed in YAML Front-matter for .hbs files


I have been communicating back and forth with the Assemble team on this issue which they think seems to be an implementation issue rather than a bug.

Basically I have an assemble setup (not grunt-assemble) and am trying to pull in some data from a YAML file into a handlebars template via assembles YAML front-matter, passing it to a partial to render out.

What I'm getting when logging assembles data context is that the front matter variables are undefined.

Rather than go through everything I've done and code snippets here you can see this thread for a history of the conversation, code snippets and all things tried thus far: https://github.com/assemble/assemble/issues/758

I have also created a public demo repo on my github for anyone that wants to pull it down / fork for further investigation.

https://github.com/tgdev/assemble-yaml-demo

Why does the lodash template in the front matter return undefined, rather than the contents of the external YAML file?

UPDATE 11/09/16: Using json seems to work fine

Changing the data in the yml files to json (with .json extension) renders as expected with the front-matter middleware in tools/templates.js so it seems as though assemble and/or handlebars is having an issue parsing the yml files.

Here is a copy of the main yml file I'm testing with:

content: >
  <h2>Page sub heading</h2>
  <p>Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.</p>
  <p>
  <img src="http://placehold.it/1170x658" alt="" class="align-left" />
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>
  <p><img src="http://placehold.it/1170x658" alt="" class="align-right" />
Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.
  </p>

So now, my question is why can't the lodash templates in the assemble front-matter of the handlebars template parse the yaml files (undefined in logs)?


Solution

  • Ok so I finally got to the bottom of it.

    The problem was this...

    Assemble doesn't support external yaml font files so I needed to use this snippet of code just before my middleware.

    import yaml from 'js-yaml';
    
    templates.dataLoader('yml', function(str) {
      return yaml.safeLoad(str);
    });
    

    Then the expander middleware loads the yaml file contents via a lodash template from the front-matter, adding it to the data context.

    So here's the whole assemble build task file for reference (I hope it helps someone in the future).

    import assemble from 'assemble';
    import expander from 'expander';
    import merge from 'lodash.merge';
    import yaml from 'js-yaml';
    import through from 'through2';
    import plumber from 'gulp-plumber';
    import { projectName, templatesPathConfig as path } from '../project.config';
    
    export default function templates() {
    
      // Create assemble instance
      let templates = assemble();
    
      function expand(data) {
          // `data` is front-matter
          const ctx = merge({}, templates.cache.data, data);
          return expander.process(ctx, data);
      }
    
      // @reference
      // https://github.com/node-base/base-data#dataloader
      //
      // @notes
      // Loading yaml files is not built in. Assemble uses
      // base-data now. You can add yaml loading by using
      // a custom dataLoader.
      templates.dataLoader('yml', function(str) {
          return yaml.safeLoad(str);
      });
    
      templates.data(path.data); // path.data = ./src/templates/data/**/*.yml
    
      templates.preRender(/\.(hbs|html)$/, function (view, next) {
          view.data = expand(view.data);
          next();
      });
    
      templates.task('preload', function(cb) {
    
          templates.partials(path.partials);
          templates.layouts(path.layouts);
    
          // Register helpers
          templates.helpers(path.helpers);
    
          // Add master pages and listing page
          templates.pages(path.pages);
          templates.pages(path.referencePages);
          templates.pages(path.index);
    
          // Add custom data
          templates.data({
              projectName: projectName
          });
    
          cb();
    
      });
    
      // Assemble task to build template files
      templates.task('build', ['preload'], () => {
    
          // Render out the template files to 'dist'
          return templates.toStream('pages')
              .pipe(plumber({
                  errorHandler: err => {
                      log.error(`${err.message}.`);
                  }
              }))
              .pipe(templates.renderFile())
              .pipe(plumber.stop())
              .pipe(renameExt())
              .pipe(templates.dest('dist'));
      });
    
      // Run the Assemble build method
      templates.build('build');
    }
    
    // Change the file extension through node stream
    function renameExt() {
        return through.obj( (file, enc, next) => {
            file.extname = '.html';
            next(null, file);
        });
    }