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;
}