All files / gulpfile.ts/lib revNapkin.ts

44.44% Statements 16/36
33.33% Branches 6/18
20% Functions 1/5
44.44% Lines 16/36

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          1x 1x 1x 1x 1x 1x 1x                 1x 1x                     1x                             1x   1x   1x 1x     1x                                                                                           1x    
/*******************************************************************************
 * Originally from GulpRevNapkin.
 * Delete Original files left from gulp-rev(-all).
 * Patched as npm package hasn't been updated... :(
 ******************************************************************************/
import chalk from 'chalk'
import * as log from 'fancy-log'
import * as fs from 'fs'
import * as path from 'path'
import * as PluginError from 'plugin-error'
import * as rimraf from 'rimraf'
import * as through from 'through2'
 
interface IOptions {
  force?: boolean
  verbose?: boolean
  fallbackCwd: string
}
 
// CONSTANT
const PLUGIN_NAME = 'gulp-rev-clean'
const createError = (msg: string) =>
  new PluginError({
    message: msg,
    plugin: PLUGIN_NAME,
  })
 
/**
 * gulp-rev-napkin
 * @param {GulpRevNapkinOpts} [options] - configuration
 * @return - NodeJS Transform Stream
 */
export const revNapkin = (options: IOptions) => {
  // Validate Input
  if (options) {
    if (!(options instanceof Object)) {
      throw createError('<options> must be typeof "object"')
    }
    if (options.verbose && typeof options.verbose !== 'boolean') {
      throw createError('<options>.verbose must be typeof "boolean"')
    }
    if (options.force && typeof options.force !== 'boolean') {
      throw createError('<options>.force must be typeof "boolean"')
    }
  }
 
  // Defaults
  options = options || {}
  if (options.verbose === undefined || options.verbose === null) {
    options.verbose = true
  }
  options.verbose = options.verbose ? true : false
  options.force = options.force ? true : false
 
  // Napkin
  const stream = through.obj(function(file, enc, callback) {
    this.push(file)
 
    if (file.isNull() || !file.revOrigPath) {
      return callback()
    }
 
    function rimrafHandler(error: Error) {
      if (error) {
        callback(createError(error.message))
        return
      }
      log(chalk.red('DELETED:'), '"' + chalk.cyan(file.revOrigPath) + '"')
      callback()
    }
 
    function lstatHandler(error: NodeJS.ErrnoException, stats: fs.Stats) {
      if (error) {
        callback(createError(error.message))
        return
      }
      if (stats.isDirectory()) {
        callback(createError('Directory removal NOT supported "' + file.revOrigPath + '"'))
        return
      }
      // For Safety: Resolve Paths
      const cwd = file.cwd || options.fallbackCwd
      const filepath = path.resolve(cwd, file.revOrigPath)
      const relativeFromCwd = path.relative(cwd, filepath)
      // Restrict to CWD: unless specified
      if (!options.force && relativeFromCwd.substr(0, 2) === '..') {
        callback(
          createError(
            'Delete outside of CWD NOT supported. To enable -> {force:true}: "' +
              file.revOrigPath +
              '"'
          )
        )
      } else {
        rimraf(file.revOrigPath, rimrafHandler)
      }
    }
 
    fs.lstat(file.revOrigPath, lstatHandler)
  })
 
  return stream
}