<commit_before>/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

import {
  BaseException,
  JsonObject,
  JsonParseMode,
  Path,
  dirname,
  getSystemPath,
  join,
  logging,
  normalize,
  parseJson,
  resolve,
  schema,
  virtualFs,
} from '@angular-devkit/core';
import { resolve as nodeResolve } from '@angular-devkit/core/node';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { _throw } from 'rxjs/observable/throw';
import { concatMap } from 'rxjs/operators';
import {
  BuildEvent,
  Builder,
  BuilderConstructor,
  BuilderContext,
  BuilderDescription,
  BuilderMap,
} from './builder';
import { Workspace } from './workspace';


export class ProjectNotFoundException extends BaseException {
  constructor(name?: string) {
    const nameOrDefault = name ? `Project '${name}'` : `Default project`;
    super(`${nameOrDefault} could not be found in workspace.`);
  }
}

export class TargetNotFoundException extends BaseException {
  constructor(name?: string) {
    const nameOrDefault = name ? `Target '${name}'` : `Default target`;
    super(`${nameOrDefault} could not be found in workspace.`);
  }
}

export class ConfigurationNotFoundException extends BaseException {
  constructor(name: string) {
    super(`Configuration '${name}' could not be found in project.`);
  }
}

export class SchemaValidationException extends BaseException {
  constructor(errors: string[]) {
    super(`Schema validation failed with the following errors:\n  ${errors.join('\n  ')}`);
  }
}

// TODO: break this exception apart into more granular ones.
export class BuilderCannotBeResolvedException extends BaseException {
  constructor(builder: string) {
    super(`Builder '${builder}' cannot be resolved.`);
  }
}

export class WorkspaceNotYetLoadedException extends BaseException {
  constructor() { super(`Workspace needs to be loaded before Architect is used.`); }
}

export interface Target<OptionsT = {}> {
  root: Path;
  projectType: string;
  builder: string;
  options: OptionsT;
}

export interface TargetOptions<OptionsT = {}> {
  project?: string;
  target?: string;
  configuration?: string;
  overrides?: Partial<OptionsT>;
}

export class Architect {
  private readonly _workspaceSchema = join(normalize(__dirname), 'workspace-schema.json');
  private readonly _buildersSchema = join(normalize(__dirname), 'builders-schema.json');
  private _workspace: Workspace;

  constructor(private _root: Path, private _host: virtualFs.Host<{}>) { }

  loadWorkspaceFromHost(workspacePath: Path) {
    return this._host.read(join(this._root, workspacePath)).pipe(
      concatMap((buffer) => {
        const json = JSON.parse(virtualFs.fileBufferToString(buffer));

        return this.loadWorkspaceFromJson(json);
      }),
    );
  }

  loadWorkspaceFromJson(json: Workspace) {
    return this._validateAgainstSchema(json, this._workspaceSchema).pipe(
      concatMap((validatedWorkspace: Workspace) => {
        this._workspace = validatedWorkspace;

        return of(this);
      }),
    );
  }

  getTarget<OptionsT>(options: TargetOptions = {}): Target<OptionsT> {
    let { project, target: targetName } = options;
    const { configuration, overrides } = options;

    if (!this._workspace) {
      throw new WorkspaceNotYetLoadedException();
    }

    project = project || this._workspace.defaultProject as string;
    const workspaceProject = this._workspace.projects[project];

    if (!workspaceProject) {
      throw new ProjectNotFoundException(project);
    }

    targetName = targetName || workspaceProject.defaultTarget as string;
    const workspaceTarget = workspaceProject.targets[targetName];

    if (!workspaceTarget) {
      throw new TargetNotFoundException(targetName);
    }

    const workspaceTargetOptions = workspaceTarget.options;
    let workspaceConfiguration;

    if (configuration) {
      workspaceConfiguration = workspaceTarget.configurations
        && workspaceTarget.configurations[configuration];

      if (!workspaceConfiguration) {
        throw new ConfigurationNotFoundException(configuration);
      }
    }

    // Resolve root for the target.
    // TODO: add Path format to JSON schemas
    const target: Target<OptionsT> = {
      root: resolve(this._root, normalize(workspaceProject.root)),
      projectType: workspaceProject.projectType,
      builder: workspaceTarget.builder,
      options: {
        ...workspaceTargetOptions,
        ...workspaceConfiguration,
        ...overrides as {},
      } as OptionsT,
    };

    return target;
  }

  // Will run the target using the target.
  run<OptionsT>(
    target: Target<OptionsT>,
    partialContext: Partial<BuilderContext> = {},
  ): Observable<BuildEvent> {
    const context: BuilderContext = {
      logger: new logging.NullLogger(),
      architect: this,
      host: this._host,
      ...partialContext,
    };

    let builderDescription: BuilderDescription;

    return this.getBuilderDescription(target).pipe(
      concatMap(description => {
        builderDescription = description;

        return this.validateBuilderOptions(target, builderDescription);
      }),
      concatMap(() => of(this.getBuilder(builderDescription, context))),<commit_msg>fix(@angular-devkit/architect): return copy of target props<commit_after>/**
 * @license
 * Copyright Google Inc. All Rights Reserved.
 *
 * Use of this source code is governed by an MIT-style license that can be
 * found in the LICENSE file at https://angular.io/license
 */

import {
  BaseException,
  JsonObject,
  JsonParseMode,
  Path,
  dirname,
  getSystemPath,
  join,
  logging,
  normalize,
  parseJson,
  resolve,
  schema,
  virtualFs,
} from '@angular-devkit/core';
import { resolve as nodeResolve } from '@angular-devkit/core/node';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { _throw } from 'rxjs/observable/throw';
import { concatMap } from 'rxjs/operators';
import {
  BuildEvent,
  Builder,
  BuilderConstructor,
  BuilderContext,
  BuilderDescription,
  BuilderMap,
} from './builder';
import { Workspace } from './workspace';


export class ProjectNotFoundException extends BaseException {
  constructor(name?: string) {
    const nameOrDefault = name ? `Project '${name}'` : `Default project`;
    super(`${nameOrDefault} could not be found in workspace.`);
  }
}

export class TargetNotFoundException extends BaseException {
  constructor(name?: string) {
    const nameOrDefault = name ? `Target '${name}'` : `Default target`;
    super(`${nameOrDefault} could not be found in workspace.`);
  }
}

export class ConfigurationNotFoundException extends BaseException {
  constructor(name: string) {
    super(`Configuration '${name}' could not be found in project.`);
  }
}

export class SchemaValidationException extends BaseException {
  constructor(errors: string[]) {
    super(`Schema validation failed with the following errors:\n  ${errors.join('\n  ')}`);
  }
}

// TODO: break this exception apart into more granular ones.
export class BuilderCannotBeResolvedException extends BaseException {
  constructor(builder: string) {
    super(`Builder '${builder}' cannot be resolved.`);
  }
}

export class WorkspaceNotYetLoadedException extends BaseException {
  constructor() { super(`Workspace needs to be loaded before Architect is used.`); }
}

export interface Target<OptionsT = {}> {
  root: Path;
  projectType: string;
  builder: string;
  options: OptionsT;
}

export interface TargetOptions<OptionsT = {}> {
  project?: string;
  target?: string;
  configuration?: string;
  overrides?: Partial<OptionsT>;
}

export class Architect {
  private readonly _workspaceSchema = join(normalize(__dirname), 'workspace-schema.json');
  private readonly _buildersSchema = join(normalize(__dirname), 'builders-schema.json');
  private _workspace: Workspace;

  constructor(private _root: Path, private _host: virtualFs.Host<{}>) { }

  loadWorkspaceFromHost(workspacePath: Path) {
    return this._host.read(join(this._root, workspacePath)).pipe(
      concatMap((buffer) => {
        const json = JSON.parse(virtualFs.fileBufferToString(buffer));

        return this.loadWorkspaceFromJson(json);
      }),
    );
  }

  loadWorkspaceFromJson(json: Workspace) {
    return this._validateAgainstSchema(json, this._workspaceSchema).pipe(
      concatMap((validatedWorkspace: Workspace) => {
        this._workspace = validatedWorkspace;

        return of(this);
      }),
    );
  }

  getTarget<OptionsT>(options: TargetOptions = {}): Target<OptionsT> {
    let { project, target: targetName } = options;
    const { configuration, overrides } = options;

    if (!this._workspace) {
      throw new WorkspaceNotYetLoadedException();
    }

    project = project || this._workspace.defaultProject as string;
    const workspaceProject = this._workspace.projects[project];

    if (!workspaceProject) {
      throw new ProjectNotFoundException(project);
    }

    targetName = targetName || workspaceProject.defaultTarget as string;
    const workspaceTarget = workspaceProject.targets[targetName];

    if (!workspaceTarget) {
      throw new TargetNotFoundException(targetName);
    }

    const workspaceTargetOptions = workspaceTarget.options;
    let workspaceConfiguration;

    if (configuration) {
      workspaceConfiguration = workspaceTarget.configurations
        && workspaceTarget.configurations[configuration];

      if (!workspaceConfiguration) {
        throw new ConfigurationNotFoundException(configuration);
      }
    }

    // Resolve root for the target.
    // TODO: add Path format to JSON schemas
    const target: Target<OptionsT> = {
      root: resolve(this._root, normalize(workspaceProject.root)),
      projectType: workspaceProject.projectType,
      builder: workspaceTarget.builder,
      options: {
        ...workspaceTargetOptions,
        ...workspaceConfiguration,
        ...overrides as {},
      } as OptionsT,
    };

    // Return a copy of the target object, JSON validation changes objects and we don't
    // want the original properties to be modified.
    return JSON.parse(JSON.stringify(target));
  }

  // Will run the target using the target.
  run<OptionsT>(
    target: Target<OptionsT>,
    partialContext: Partial<BuilderContext> = {},
  ): Observable<BuildEvent> {
    const context: BuilderContext = {
      logger: new logging.NullLogger(),
      architect: this,
      host: this._host,
      ...partialContext,
    };

    let builderDescription: BuilderDescription;

    return this.getBuilderDescription(target).pipe(
      concatMap(description => {
        builderDescription = description;

        return this.validateBuilderOptions(target, builderDescription);
      }),
      concatMap(() => of(this.getBuilder(builderDescription, context))),
{'fraction_non_alphanumeric': 0.10986506199854121, 'fraction_numerical': 0.0, 'mean_word_length': 3.8194200351493848, 'pattern_counts': {'":': 0, '<': 25, '<?xml version=': 0, '>': 33, 'https://': 2, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '2974343', 'n_tokens_mistral': 2976, 'n_tokens_neox': 2938, 'n_words': 789}