code: function layouts(file, layoutCollection, options, transformFn) {
    if (typeOf(file) !== 'object') {
      throw new TypeError('expected file to be an object');
    }
  
    if (typeOf(layoutCollection) !== 'object' && !(layoutCollection instanceof Map)) {
      throw new TypeError('expected layouts collection to be an object');
    }
  
    if (typeOf(file.contents) !== 'buffer') {
      throw new TypeError('expected file.contents to be a buffer');
    }
  
    if (typeof options === 'function') {
      transformFn = options;
      options = null;
    }
  
    const opts = Object.assign({ tagname: 'body' }, options, file.options);
    const regex = createDelimiterRegex(opts);
    let name = getLayoutName(file, opts);
    let layout;
    let n = 0;
  
    if (!name) return file;
  
    define(file, 'layoutStack', file.layoutStack || []);
  
    // recursively resolve layouts
    while ((layout = getLayout(layoutCollection, name))) {
      if (inHistory(file, layout, opts)) break;
  
      // if a function is passed, call it on the file before resolving the next layout
      if (typeof transformFn === 'function') {
        transformFn(file, layout);
      }
  
      file.layoutStack.push(layout);
      name = resolveLayout(file, layout, opts, regex, name);
      n++;
    }
  
    if (n === 0) {
      let filename = file.relative || file.path;
      throw new Error(`layout "${name}" is defined on "${filename}" but cannot be found`);
    }
  
    return file;
  }
docs: Apply a layout from the `layouts` collection to `file.contents`. Layouts will be
  recursively applied until a layout is not defined by the returned file.
  
  @param {Object} `file` File object. This can be a plain object or vinyl file.
  @param {Object} `layoutCollection` Collection of file objects to use as layouts.
  @param {Object} `options`
  @return {Object} Returns the original file object with layout(s) applied.