11cb0ef41Sopenharmony_ciconst parseJSON = require('json-parse-even-better-errors')
21cb0ef41Sopenharmony_ciconst { diff } = require('just-diff')
31cb0ef41Sopenharmony_ciconst { diffApply } = require('just-diff-apply')
41cb0ef41Sopenharmony_ci
51cb0ef41Sopenharmony_ciconst globalObjectProperties = Object.getOwnPropertyNames(Object.prototype)
61cb0ef41Sopenharmony_ci
71cb0ef41Sopenharmony_ciconst stripBOM = content => {
81cb0ef41Sopenharmony_ci  content = content.toString()
91cb0ef41Sopenharmony_ci  // Remove byte order marker. This catches EF BB BF (the UTF-8 BOM)
101cb0ef41Sopenharmony_ci  // because the buffer-to-string conversion in `fs.readFileSync()`
111cb0ef41Sopenharmony_ci  // translates it to FEFF, the UTF-16 BOM.
121cb0ef41Sopenharmony_ci  if (content.charCodeAt(0) === 0xFEFF) {
131cb0ef41Sopenharmony_ci    content = content.slice(1)
141cb0ef41Sopenharmony_ci  }
151cb0ef41Sopenharmony_ci  return content
161cb0ef41Sopenharmony_ci}
171cb0ef41Sopenharmony_ci
181cb0ef41Sopenharmony_ciconst PARENT_RE = /\|{7,}/g
191cb0ef41Sopenharmony_ciconst OURS_RE = /<{7,}/g
201cb0ef41Sopenharmony_ciconst THEIRS_RE = /={7,}/g
211cb0ef41Sopenharmony_ciconst END_RE = />{7,}/g
221cb0ef41Sopenharmony_ci
231cb0ef41Sopenharmony_ciconst isDiff = str =>
241cb0ef41Sopenharmony_ci  str.match(OURS_RE) && str.match(THEIRS_RE) && str.match(END_RE)
251cb0ef41Sopenharmony_ci
261cb0ef41Sopenharmony_ciconst parseConflictJSON = (str, reviver, prefer) => {
271cb0ef41Sopenharmony_ci  prefer = prefer || 'ours'
281cb0ef41Sopenharmony_ci  if (prefer !== 'theirs' && prefer !== 'ours') {
291cb0ef41Sopenharmony_ci    throw new TypeError('prefer param must be "ours" or "theirs" if set')
301cb0ef41Sopenharmony_ci  }
311cb0ef41Sopenharmony_ci
321cb0ef41Sopenharmony_ci  str = stripBOM(str)
331cb0ef41Sopenharmony_ci
341cb0ef41Sopenharmony_ci  if (!isDiff(str)) {
351cb0ef41Sopenharmony_ci    return parseJSON(str)
361cb0ef41Sopenharmony_ci  }
371cb0ef41Sopenharmony_ci
381cb0ef41Sopenharmony_ci  const pieces = str.split(/[\n\r]+/g).reduce((acc, line) => {
391cb0ef41Sopenharmony_ci    if (line.match(PARENT_RE)) {
401cb0ef41Sopenharmony_ci      acc.state = 'parent'
411cb0ef41Sopenharmony_ci    } else if (line.match(OURS_RE)) {
421cb0ef41Sopenharmony_ci      acc.state = 'ours'
431cb0ef41Sopenharmony_ci    } else if (line.match(THEIRS_RE)) {
441cb0ef41Sopenharmony_ci      acc.state = 'theirs'
451cb0ef41Sopenharmony_ci    } else if (line.match(END_RE)) {
461cb0ef41Sopenharmony_ci      acc.state = 'top'
471cb0ef41Sopenharmony_ci    } else {
481cb0ef41Sopenharmony_ci      if (acc.state === 'top' || acc.state === 'ours') {
491cb0ef41Sopenharmony_ci        acc.ours += line
501cb0ef41Sopenharmony_ci      }
511cb0ef41Sopenharmony_ci      if (acc.state === 'top' || acc.state === 'theirs') {
521cb0ef41Sopenharmony_ci        acc.theirs += line
531cb0ef41Sopenharmony_ci      }
541cb0ef41Sopenharmony_ci      if (acc.state === 'top' || acc.state === 'parent') {
551cb0ef41Sopenharmony_ci        acc.parent += line
561cb0ef41Sopenharmony_ci      }
571cb0ef41Sopenharmony_ci    }
581cb0ef41Sopenharmony_ci    return acc
591cb0ef41Sopenharmony_ci  }, {
601cb0ef41Sopenharmony_ci    state: 'top',
611cb0ef41Sopenharmony_ci    ours: '',
621cb0ef41Sopenharmony_ci    theirs: '',
631cb0ef41Sopenharmony_ci    parent: '',
641cb0ef41Sopenharmony_ci  })
651cb0ef41Sopenharmony_ci
661cb0ef41Sopenharmony_ci  // this will throw if either piece is not valid JSON, that's intended
671cb0ef41Sopenharmony_ci  const parent = parseJSON(pieces.parent, reviver)
681cb0ef41Sopenharmony_ci  const ours = parseJSON(pieces.ours, reviver)
691cb0ef41Sopenharmony_ci  const theirs = parseJSON(pieces.theirs, reviver)
701cb0ef41Sopenharmony_ci
711cb0ef41Sopenharmony_ci  return prefer === 'ours'
721cb0ef41Sopenharmony_ci    ? resolve(parent, ours, theirs)
731cb0ef41Sopenharmony_ci    : resolve(parent, theirs, ours)
741cb0ef41Sopenharmony_ci}
751cb0ef41Sopenharmony_ci
761cb0ef41Sopenharmony_ciconst isObj = obj => obj && typeof obj === 'object'
771cb0ef41Sopenharmony_ci
781cb0ef41Sopenharmony_ciconst copyPath = (to, from, path, i) => {
791cb0ef41Sopenharmony_ci  const p = path[i]
801cb0ef41Sopenharmony_ci  if (isObj(to[p]) && isObj(from[p]) &&
811cb0ef41Sopenharmony_ci      Array.isArray(to[p]) === Array.isArray(from[p])) {
821cb0ef41Sopenharmony_ci    return copyPath(to[p], from[p], path, i + 1)
831cb0ef41Sopenharmony_ci  }
841cb0ef41Sopenharmony_ci  to[p] = from[p]
851cb0ef41Sopenharmony_ci}
861cb0ef41Sopenharmony_ci
871cb0ef41Sopenharmony_ci// get the diff from parent->ours and applying our changes on top of theirs.
881cb0ef41Sopenharmony_ci// If they turned an object into a non-object, then put it back.
891cb0ef41Sopenharmony_ciconst resolve = (parent, ours, theirs) => {
901cb0ef41Sopenharmony_ci  const dours = diff(parent, ours)
911cb0ef41Sopenharmony_ci  for (let i = 0; i < dours.length; i++) {
921cb0ef41Sopenharmony_ci    if (globalObjectProperties.find(prop => dours[i].path.includes(prop))) {
931cb0ef41Sopenharmony_ci      continue
941cb0ef41Sopenharmony_ci    }
951cb0ef41Sopenharmony_ci    try {
961cb0ef41Sopenharmony_ci      diffApply(theirs, [dours[i]])
971cb0ef41Sopenharmony_ci    } catch (e) {
981cb0ef41Sopenharmony_ci      copyPath(theirs, ours, dours[i].path, 0)
991cb0ef41Sopenharmony_ci    }
1001cb0ef41Sopenharmony_ci  }
1011cb0ef41Sopenharmony_ci  return theirs
1021cb0ef41Sopenharmony_ci}
1031cb0ef41Sopenharmony_ci
1041cb0ef41Sopenharmony_cimodule.exports = Object.assign(parseConflictJSON, { isDiff })
105