function parseProject()

in packages/angular_devkit/core/src/workspace/json/reader.ts [162:274]


function parseProject(
  projectName: string,
  projectNode: JsonAstObject,
  context: ParserContext,
): ProjectDefinition {
  const jsonMetadata = context.metadata;
  let targets;
  let targetsNode: JsonAstObject | undefined;
  let extensions: Record<string, JsonValue> | undefined;
  let properties: Record<'root' | 'sourceRoot' | 'prefix', string> | undefined;
  if (!context.trackChanges) {
    // If not tracking changes, the parser will store the values directly in standard objects
    extensions = Object.create(null);
    properties = Object.create(null);
  }

  for (const { key, value } of projectNode.properties) {
    const name = key.value;
    switch (name) {
      case 'targets':
      case 'architect':
        if (value.kind !== 'object') {
          context.error(`Invalid "${name}" field found; expected an object.`, value);
          break;
        }
        targetsNode = value;
        targets = parseTargetsObject(projectName, value, context);
        break;
      case 'prefix':
      case 'root':
      case 'sourceRoot':
        if (value.kind !== 'string') {
          context.warn(`Project property "${name}" should be a string.`, value);
        }
        if (properties) {
          properties[name] = value.value as string;
        }
        break;
      default:
        if (!specialProjectExtensions.includes(name) && !/^[a-z]{1,3}-.*/.test(name)) {
          context.warn(`Project extension with invalid name found.`, key);
        }
        if (extensions) {
          extensions[name] = value.value;
        }
        break;
    }
  }

  let collectionListener: DefinitionCollectionListener<TargetDefinition> | undefined;
  if (context.trackChanges) {
    if (targetsNode) {
      const parentNode = targetsNode;
      collectionListener = (name, action, newValue) => {
        jsonMetadata.addChange(
          action,
          `/projects/${projectName}/targets/${escapeKey(name)}`,
          parentNode,
          newValue,
          'target',
        );
      };
    } else {
      let added = false;
      collectionListener = (_name, action, _new, _old, collection) => {
        if (added || action !== 'add') {
          return;
        }

        jsonMetadata.addChange(
          'add',
          `/projects/${projectName}/targets`,
          projectNode,
          collection,
          'targetcollection',
        );
        added = true;
      };
    }
  }

  const base = {
    targets: new TargetDefinitionCollection(targets, collectionListener),
    // If not tracking changes the `extensions` variable will contain the parsed
    // values.  Otherwise the extensions are tracked via a virtual AST object.
    extensions:
      extensions ||
      createVirtualAstObject(projectNode, {
        exclude: ['architect', 'prefix', 'root', 'sourceRoot', 'targets'],
        listener(op, path, node, value) {
          jsonMetadata.addChange(op, `/projects/${projectName}${path}`, node, value);
        },
      }),
  };

  let project: ProjectDefinition;
  if (context.trackChanges) {
    project = createVirtualAstObject<ProjectDefinition>(projectNode, {
      base,
      include: ['prefix', 'root', 'sourceRoot'],
      listener(op, path, node, value) {
        jsonMetadata.addChange(op, `/projects/${projectName}${path}`, node, value);
      },
    });
  } else {
    project = {
      ...base,
      ...properties,
    } as ProjectDefinition;
  }

  return project;
}