All files / json-patch/codegen apply.ts

100% Statements 53/53
100% Branches 7/7
100% Functions 3/3
100% Lines 45/45

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 814x   4x 4x       4x 4x   4x 321x 321x 321x 321x 321x 329x 329x 256x   248x     4x       321x 321x 321x 321x   321x   321x 330x 330x 330x 330x     321x   321x 321x   321x 2x 2x     321x   321x 330x 330x 330x 330x 330x     321x               321x           4x 321x 321x 321x    
import {clone as deepClone} from '@jsonjoy.com/util/lib/json-clone/clone';
import type {Operation} from '../types';
import {operationToOp} from '../codec/json';
import {AbstractPredicateOp} from '../op';
import type {ApplyPatchOptions} from '../applyPatch/types';
import type {JsonPatchOptions} from '..';
import type {ApplyFn} from './types';
import {compile, type JavaScriptLinked, type JavaScript} from '@jsonjoy.com/util/lib/codegen';
import {codegenOp} from './codegenOp';
 
export const apply = (patch: readonly Operation[], applyOptions: ApplyPatchOptions, doc: unknown): unknown => {
  const {mutate, createMatcher} = applyOptions;
  if (!mutate) doc = deepClone(doc);
  const length = patch.length;
  const opts: JsonPatchOptions = {createMatcher};
  for (let i = 0; i < length; i++) {
    const op = operationToOp(patch[i], opts);
    const opResult = op.apply(doc);
    doc = opResult.doc;
  }
  return doc;
};
 
export const $$apply = (
  operations: readonly Operation[],
  applyOptions: ApplyPatchOptions,
): JavaScriptLinked<ApplyFn> => {
  const {mutate, createMatcher} = applyOptions;
  const operationOptions: JsonPatchOptions = {createMatcher};
  const fns: ApplyFn[] = [];
  const length = operations.length;
 
  let hasNonPredicateOperations = false;
 
  for (let i = 0; i < length; i++) {
    const op = operationToOp(operations[i], operationOptions);
    const isPredicateOp = op instanceof AbstractPredicateOp;
    if (!isPredicateOp) hasNonPredicateOperations = true;
    fns.push(codegenOp(op));
  }
 
  const needsToClone = !mutate && hasNonPredicateOperations;
 
  const deps: unknown[] = [];
  const depNames: string[] = [];
 
  if (needsToClone) {
    deps.push(deepClone);
    depNames.push('clone');
  }
 
  let resultExpression = 'doc';
 
  for (let i = 0; i < fns.length; i++) {
    const fn = fns[i];
    const depName = `fn${i}`;
    deps.push(fn);
    depNames.push(depName);
    resultExpression = `${depName}(${resultExpression})`;
  }
 
  const js = /* js */ `
(function(${depNames.join(',')}) {
  return function(doc){
    ${needsToClone ? /* js */ 'doc = clone(doc);' : ''}
    return ${resultExpression};
  };
})`;
 
  return {
    deps,
    js: js as JavaScript<(...deps: unknown[]) => ApplyFn>,
  };
};
 
export const $apply = (operations: readonly Operation[], applyOptions: ApplyPatchOptions): ApplyFn => {
  const fn = $$apply(operations, applyOptions);
  const compiled = compile(fn.js)(...fn.deps);
  return compiled;
};