All files / src/class rule.ts

94.29% Statements 66/70
81.4% Branches 35/43
100% Functions 9/9
96.55% Lines 56/58

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 1215x 5x 5x 5x 5x 5x 5x     5x 5x   5x                                   136x 136x 136x 136x 136x       56x   56x             56x   56x 56x 56x 56x 56x 16x 8x 8x 8x         56x 8x 8x 48x 16x     56x       528x 32x       72x 72x 72x 72x     72x 72x   72x 72x   72x       80x 968x 968x   96x             80x 80x     80x 80x 80x   80x 72x     80x      
import Ajv from 'ajv'
import glob from 'micromatch'
import { UpgradeType, Encoding, InferEncodingFunc } from '@/consts'
import { RuleSchema } from '@/schema'
import { inferEncodingByMapping, inferEncodingImmobile, inferEncodingNormally } from '@/infer-encoding'
import { Handler, Handlers, File, buildInHandlers, Maybe } from '@/internal'
import { isAbsolute } from 'path'
 
 
const ajv = new Ajv({ useDefaults: true })
const validate = ajv.compile(RuleSchema)
 
export class Rule {
  public readonly path: string
 
  public readonly upgrade: UpgradeType
 
  public readonly glob: boolean
 
  public readonly handler?: Handler
 
  public readonly inferEncoding: InferEncodingFunc
 
  constructor(
    path: string,
    upgrade: UpgradeType = UpgradeType.Cover,
    glob: boolean = true,
    inferEncoding: InferEncodingFunc = inferEncodingNormally,
    handler?: Handler,
  ) {
    this.path = path
    this.inferEncoding = inferEncoding
    this.upgrade = upgrade
    this.glob = glob
    this.handler = handler
  }
 
  public static format(obj): Rule {
    const valid = validate(obj)
 
    Iif (!valid) {
      throw new TypeError([
        'Incorrect rules field configuration for template configuration',
        ajv.errorsText(validate.errors, { dataVar: 'rule' }),
      ].join('\n'))
    }
 
    Iif (!isAbsolute(obj.path)) throw new TypeError(`The path of rule should be absolute path. But get ${obj.path}`)
 
    const path = obj.path
    const upgrade = obj.upgrade
    const glob = obj.glob
    let inferEncoding: InferEncodingFunc = inferEncodingNormally
    if ('encoding' in obj) {
      if (typeof obj.encoding === 'object') {
        inferEncoding = inferEncodingByMapping(obj.encoding)
      } else Eif (Object.values(Encoding).includes(obj.encoding)) {
        inferEncoding = inferEncodingImmobile(obj.encoding)
      }
    }
    let handler
 
    if (obj.handlers) {
      const handlers = obj.handlers.map(Handler.format)
      handler = Handler.compose(handlers)
    } else if (obj.handler) {
      handler = Handler.format(obj.handler)
    }
 
    return new Rule(path, upgrade, glob, inferEncoding, handler)
  }
 
  public match(path: string): boolean {
    if (this.glob) return glob.isMatch(path, this.path)
    return this.path === path
  }
 
  public static merge(parent: Rule, child: Rule): Rule {
    const path = child.path
    const upgrade = child.upgrade
    const glob = child.glob
    const handlers: Handlers = []
    let handler: Maybe<Handler>
 
    Iif (parent.handler) handlers.push(parent.handler)
    if (child.handler) handlers.push(child.handler)
 
    if (handlers.length) handler = Handler.compose(handlers)
    const inferEncoding = child.inferEncoding || parent.inferEncoding
 
    return new Rule(path, upgrade, glob, inferEncoding, handler)
  }
 
  public createFile(path: string): File {
    const paths = path.split('/')
      .map((pair, i, arr) => arr.slice(0, arr.length - i).join('/'))
      .filter(item => Boolean(item))
 
    Iif (!paths.some(item => this.match(item))) {
      throw new Error([
        'Cannot create file from rule',
        'Because the file path is not match rule',
      ].join('\n'))
    }
 
    const { inferEncoding, upgrade } = this
    let handler: Maybe<Handler> = this.handler
    let upgradeHandler: Maybe<Handler>
 
    if (upgrade === 'merge') upgradeHandler = buildInHandlers.merge
    if (upgrade === 'exist') upgradeHandler = buildInHandlers.exist
    if (upgrade === 'keep') upgradeHandler = buildInHandlers.ignoreWhen(({ operation }) => operation === 'init')
 
    if (handler && upgradeHandler) handler = Handler.compose([handler, upgradeHandler])
    else if (!handler && upgradeHandler) handler = upgradeHandler
 
 
    return new File(path, inferEncoding, handler)
  }
}