function create()

in packages/angular_devkit/core/src/workspace/json/utilities.ts [113:299]


function create(
  ast: JsonAstObject | JsonAstArray,
  path: string,
  reporter: ChangeReporter,
  excluded = new Set<ProxyPropertyKey>(),
  included?: Set<ProxyPropertyKey>,
  base?: object,
) {
  const cache = new Map<string, CacheEntry>();
  const alteredNodes = new Set<JsonAstNode>();

  if (!base) {
    if (ast.kind === 'object') {
      base = Object.create(null) as object;
    } else {
      base = [];
      (base as Array<unknown>).length = ast.elements.length;
    }
  }

  return new Proxy(base, {
    getOwnPropertyDescriptor(target: {}, p: ProxyPropertyKey): PropertyDescriptor | undefined {
      const descriptor = Reflect.getOwnPropertyDescriptor(target, p);
      if (descriptor || typeof p === 'symbol') {
        return descriptor;
      } else if (excluded.has(p) || (included && !included.has(p))) {
        return undefined;
      }

      const propertyPath = path + '/' + escapeKey(p);
      const cacheEntry = cache.get(propertyPath);
      if (cacheEntry) {
        if (cacheEntry.value !== undefined) {
          return createPropertyDescriptor(cacheEntry.value);
        }

        return undefined;
      }

      const { node } = findNode(ast, p);
      if (node) {
        return createPropertyDescriptor(node.value);
      }

      return undefined;
    },
    has(target: {}, p: ProxyPropertyKey): boolean {
      if (Reflect.has(target, p)) {
        return true;
      } else if (typeof p === 'symbol' || excluded.has(p)) {
        return false;
      }

      return cache.has(path + '/' + escapeKey(p)) || findNode(ast, p) !== undefined;
    },
    get(target: {}, p: ProxyPropertyKey): unknown {
      if (typeof p === 'symbol' || Reflect.has(target, p)) {
        return Reflect.get(target, p);
      } else if (excluded.has(p) || (included && !included.has(p))) {
        return undefined;
      }

      const propertyPath = path + '/' + escapeKey(p);
      const cacheEntry = cache.get(propertyPath);
      if (cacheEntry) {
        return cacheEntry.value;
      }

      const { node, parent } = findNode(ast, p);
      let value;
      if (node) {
        if (node.kind === 'object' || node.kind === 'array') {
          value = create(node, propertyPath, (path, parent, vnode, old, current) => {
            if (!alteredNodes.has(node)) {
              reporter(path, parent, vnode, old, current);
            }
          });
        } else {
          value = node.value;
        }

        cache.set(propertyPath, { node, parent, value });
      }

      return value;
    },
    set(target: {}, p: ProxyPropertyKey, value: unknown): boolean {
      if (value === undefined) {
        // setting to undefined is equivalent to a delete
        // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
        return this.deleteProperty!(target, p);
      }

      if (typeof p === 'symbol' || Reflect.has(target, p)) {
        return Reflect.set(target, p, value);
      } else if (excluded.has(p) || (included && !included.has(p))) {
        return false;
      }

      // TODO: Check if is JSON value
      const jsonValue = value as JsonValue;

      const propertyPath = path + '/' + escapeKey(p);
      const cacheEntry = cache.get(propertyPath);
      if (cacheEntry) {
        const oldValue = cacheEntry.value;
        cacheEntry.value = value as JsonValue;
        if (cacheEntry.node && oldValue !== value) {
          alteredNodes.add(cacheEntry.node);
        }
        reporter(propertyPath, cacheEntry.parent, cacheEntry.node, oldValue, jsonValue);
      } else {
        const { node, parent } = findNode(ast, p);
        cache.set(propertyPath, { node, parent, value: value as JsonValue });
        if (node && node.value !== value) {
          alteredNodes.add(node);
        }
        reporter(propertyPath, parent, node, node && node.value, value as JsonValue);
      }

      return true;
    },
    deleteProperty(target: {}, p: ProxyPropertyKey): boolean {
      if (typeof p === 'symbol' || Reflect.has(target, p)) {
        return Reflect.deleteProperty(target, p);
      } else if (excluded.has(p) || (included && !included.has(p))) {
        return false;
      }

      const propertyPath = path + '/' + escapeKey(p);
      const cacheEntry = cache.get(propertyPath);
      if (cacheEntry) {
        const oldValue = cacheEntry.value;
        cacheEntry.value = undefined;
        if (cacheEntry.node) {
          alteredNodes.add(cacheEntry.node);
        }
        if (cacheEntry.parent.kind === 'keyvalue') {
          // Remove the entire key/value pair from this JSON object
          reporter(propertyPath, ast, cacheEntry.node, oldValue, undefined);
        } else {
          reporter(propertyPath, cacheEntry.parent, cacheEntry.node, oldValue, undefined);
        }
      } else {
        const { node, parent } = findNode(ast, p);
        if (node) {
          cache.set(propertyPath, { node, parent, value: undefined });
          alteredNodes.add(node);
          if (parent.kind === 'keyvalue') {
            // Remove the entire key/value pair from this JSON object
            reporter(propertyPath, ast, node, node && node.value, undefined);
          } else {
            reporter(propertyPath, parent, node, node && node.value, undefined);
          }
        }
      }

      return true;
    },
    defineProperty(target: {}, p: ProxyPropertyKey, attributes: PropertyDescriptor): boolean {
      if (typeof p === 'symbol') {
        return Reflect.defineProperty(target, p, attributes);
      }

      return false;
    },
    ownKeys(target: {}): ProxyPropertyKey[] {
      let keys: ProxyPropertyKey[];
      if (ast.kind === 'object') {
        keys = ast.properties
          .map((entry) => entry.key.value)
          .filter((p) => !excluded.has(p) && (!included || included.has(p)));
      } else {
        keys = [];
      }

      for (const key of cache.keys()) {
        const relativeKey = key.substr(path.length + 1);
        if (relativeKey.length > 0 && !relativeKey.includes('/')) {
          keys.push(`${unescapeKey(relativeKey)}`);
        }
      }

      return [...new Set([...keys, ...Reflect.ownKeys(target)])];
    },
  });
}