All files index.js

100% Statements 59/59
100% Branches 18/18
100% Functions 9/9
100% Lines 57/57

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 1392x 2x 2x 2x             25x 25x   25x   25x 25x 25x     25x 11x 11x 11x       14x     14x 11x 11x       14x   14x       13x 13x     13x 13x   13x     13x     1x 1x                 17x     17x 3x 3x       14x 1x 1x       13x 13x   13x 1x     13x             13x 13x         13x 13x     13x 1x             13x     17x     12x   2x 2x 1x 1x   1x         12x   12x     11x      
const debug = require('debug')('metalsmith-in-place');
const match = require('multimatch');
const isUtf8 = require('is-utf8');
const getTransformer = require('./get-transformer');
 
/**
 * Engine, renders file contents with all available transformers
 */
 
function render({ filename, files, metadata, engineOptions }) {
  const [base, ...extensions] = filename.split('.');
  const file = files[filename];
 
  debug(`rendering ${filename}`);
 
  const ext = extensions.pop();
  const transform = getTransformer(ext);
  const locals = Object.assign({}, metadata, file);
 
  // Stop if the current extension can't be transformed
  if (!transform) {
    debug(`no transformer available for ${ext} extension for ${filename}`);
    extensions.push(ext);
    return Promise.resolve();
  }
 
  // Stringify file contents
  file.contents = file.contents.toString();
 
  // If this is the last extension, replace it with a new one
  if (extensions.length === 0) {
    debug(`last extension reached, replacing last extension with ${transform.outputFormat}`);
    extensions.push(transform.outputFormat);
  }
 
  // Transform the contents
  debug(`rendering ${ext} extension for ${filename}`);
 
  return transform
    .renderAsync(file.contents, engineOptions, locals)
    .then(rendered => {
      // Update contents and delete old file
      file.contents = Buffer.from(rendered.body);
      delete files[filename]; // eslint-disable-line no-param-reassign
 
      // Update files with the newly rendered file
      const newName = [base, ...extensions].join('.');
      files[newName] = file; // eslint-disable-line no-param-reassign
 
      debug(`done rendering ${filename}, renamed to ${newName}`);
 
      // Keep rendering until there are no applicable transformers left
      return render({ filename: newName, files, metadata, engineOptions });
    })
    .catch(err => {
      err.message = `${filename}: ${err.message}`; // eslint-disable-line no-param-reassign
      throw err;
    });
}
 
/**
 * Validate, checks whether a file should be processed
 */
 
function validate({ filename, files }) {
  debug(`validating ${filename}`);
 
  // Files without an extension cannot be processed
  if (!filename.includes('.')) {
    debug(`validation failed, ${filename} does not have an extension`);
    return false;
  }
 
  // Files that are not utf8 are ignored
  if (!isUtf8(files[filename].contents)) {
    debug(`validation failed, ${filename} is not utf-8`);
    return false;
  }
 
  // Files without an applicable jstransformer are ignored
  const extension = filename.split('.').pop();
  const transformer = getTransformer(extension);
 
  if (!transformer) {
    debug(`validation failed, no jstransformer found for last extension of ${filename}`);
  }
 
  return transformer;
}
 
/**
 * Plugin, the main plugin used by metalsmith
 */
 
module.exports = options => (files, metalsmith, done) => {
  const defaults = {
    pattern: '**',
    engineOptions: {},
    suppressNoFilesError: false
  };
  const settings = Object.assign({}, defaults, options);
  const metadata = metalsmith.metadata();
 
  // Check whether the pattern option is valid
  if (!(typeof settings.pattern === 'string' || Array.isArray(settings.pattern))) {
    done(
      new Error(
        'invalid pattern, the pattern option should be a string or array of strings. See https://www.npmjs.com/package/metalsmith-in-place#pattern'
      )
    );
  }
 
  const matchedFiles = match(Object.keys(files), settings.pattern);
 
  // Filter files by validity
  const validFiles = matchedFiles.filter(filename => validate({ filename, files }));
 
  // Let the user know when there are no files to process, usually caused by missing jstransformer
  if (validFiles.length === 0) {
    const msg =
      'no files to process. See https://www.npmjs.com/package/metalsmith-in-place#no-files-to-process';
    if (settings.suppressNoFilesError) {
      debug(msg);
      done();
    } else {
      done(new Error(msg));
    }
  }
 
  // Map all files that should be processed to an array of promises and call done when finished
  Promise.all(
    validFiles.map(filename =>
      render({ filename, files, metadata, engineOptions: settings.engineOptions })
    )
  )
    .then(() => done())
    .catch(/* istanbul ignore next */ error => done(error));
};