<commit_before>import {Project, RootGraph, Reporter,
  AttributePath, AttributeTypes, TargetExportsElement, FileElement, ComponentElement,
  Task, Graph, TaskName, BuildTargetElement, File, util, GenerateFileTask
} from './index.priv';
import {Hash} from 'crypto';
import * as path from 'path';
import * as fs from 'fs-extra';

export var targetClasses = new Map<string, typeof Target>();
export function declareTarget(options: { type: string }) {
  return function (constructor: typeof Target) {
    constructor.prototype.classname = options.type;
    targetClasses.set(options.type, constructor);
  };
}

export function getTargetClass(type: string) : typeof Target | undefined {
  return targetClasses.get(type);
}

export class PropertyResolver<T> {
  constructor(public validator: AttributeTypes.Validator<T, Target>, public attributePath: string, public propertyPath?: string) {}

  resolve(reporter: Reporter, into: SelfBuildGraph<any>, target: Target, path: AttributePath = new AttributePath(target)) : T | undefined {
    let attr = target.attributes[this.attributePath];
    let r: T | undefined = undefined;
    path.push('.', this.attributePath);
    if (attr !== undefined || (this.propertyPath && (r = into[this.propertyPath]) === undefined)) {
      r = this.validator(reporter, path, attr, target);
      if (this.propertyPath && r !== undefined)
        into[this.propertyPath] = r;
    }
    path.pop(2);
    return r;
  }
}

function setupResolvers(prototype: { resolvers: PropertyResolver<any>[] }) : PropertyResolver<any>[] {
  let p = prototype;
  if (!p.hasOwnProperty('resolvers'))
    p.resolvers = prototype.resolvers ? prototype.resolvers.slice() : [];
  return p.resolvers;
}

function pushResolvers(prototype: { resolvers: PropertyResolver<any>[] }, r: PropertyResolver<any>[]) {
  setupResolvers(prototype).push(...r);
}

export function declareResolvers(r: PropertyResolver<any>[]) {
  return function installResolvers(cls: { prototype: { resolvers: PropertyResolver<any>[] } }) {
    pushResolvers(cls.prototype, r);
  };
}
export function resolver<T>(r: AttributeTypes.Validator<T, Target>, options?: {
  attributePath?: string
 }) {
  return function pushResolverOnProperty(prototype: typeof SelfBuildGraph.prototype, propertyName: string, descriptor?: TypedPropertyDescriptor<T>) {
    setupResolvers(prototype).push(new PropertyResolver(r, (options && options.attributePath) || propertyName, propertyName));
  };
}

export abstract class SelfBuildGraph<P extends Graph> extends Graph {
  readonly resolvers: PropertyResolver<any>[]; // on the prototype
  graph: P;

  constructor(name: TaskName, graph: P) {
    super(name, graph);
  }

  resolve(reporter: Reporter, target: Target, path: AttributePath = new AttributePath(target)) {
    for (var r of this.resolvers) {
      r.resolve(reporter, this, target, path);
    }
  }

  buildGraph(reporter: Reporter) {}
  configureExports(reporter: Reporter) {}
}
setupResolvers(SelfBuildGraph.prototype);

const configureResolver = new PropertyResolver<void>(AttributeTypes.functionValidator("(target: Target) => void"), "configure");

export class Target extends SelfBuildGraph<RootGraph> {
  name: { type: "target", name: string, environment: string, variant: string, project: string };

  dependencies: Set<Target>;
  requiredBy: Set<Target>;

  exports: TargetExportsElement;
  exportsTask: Target.GenerateExports;
  project: Project;
  attributes: BuildTargetElement;
  paths: {
    output: string,
    build: string,
    shared: string,
    intermediates: string,
    tasks: string,
  };
  modifiers: ((reporter: Reporter, task: Task) => void)[];

  variant: string;
  environment: string;
  targetName: string;

  @resolver(AttributeTypes.validateString)
  outputName: string;

  @resolver(AttributeTypes.validateString)
  outputFinalName: string | null = null;

  constructor(graph: RootGraph, project: Project, attributes: BuildTargetElement) {
    super({
      type: "target",
      name: attributes.name,
      environment: attributes.environment.name,
      variant: attributes.variant,
      project: project.path
    }, graph);

    this.project = project;
    this.variant = attributes.variant;
    this.targetName = attributes.name;
    this.outputName = attributes.name;
    this.environment = attributes.environment.name;
    this.attributes = attributes;

    this.modifiers = [];
    let env_variant = `${this.environment}/${this.variant}`;
    let build = path.join(this.project.workspace.directory, '.build', env_variant);
    this.paths = {
      output       : path.join(this.project.workspace.directory, env_variant),
      shared       : path.join(this.project.workspace.directory, '.shared', env_variant),
      build        : build,
      intermediates: path.join(build, "intermediates", this.targetName),
      tasks        : path.join(build, "tasks")
    };
    this.exports = new TargetExportsElement(this, this.attributes.name);
    this.attributes.targets.forEach(t => t.__target && this.addDependency(t.__target));
    fs.ensureDirSync(this.paths.tasks);
  }

  __path() {
    return this.attributes.__path();
  }

  uniqueKey(hash: Hash) : boolean {
    hash.update(this.variant + "\t" + this.environment + "\t" + this.targetName);
    return true;
  }

  storagePath(task: Task) {
    var id = task.id();
    return id ? this.paths.tasks + '/' + id : null;
  }

  configure(reporter: Reporter, path: AttributePath) {
    this.resolve(reporter, this, path);
    configureResolver.resolve(reporter, this, this, path);
  }

  configureExports(reporter: Reporter) {
    let components = <ComponentElement[]>[];
    this.attributes.exports.forEach(c => {
      if (c.name)
        this.exports[`${c.name}=`] = c;
      if (c.is === 'component')
        components.push(c);
    });
  }<commit_msg>core: fix paths could be missing<commit_after>import {Project, RootGraph, Reporter,
  AttributePath, AttributeTypes, TargetExportsElement, FileElement, ComponentElement,
  Task, Graph, TaskName, BuildTargetElement, File, util, GenerateFileTask
} from './index.priv';
import {Hash} from 'crypto';
import * as path from 'path';
import * as fs from 'fs-extra';

export var targetClasses = new Map<string, typeof Target>();
export function declareTarget(options: { type: string }) {
  return function (constructor: typeof Target) {
    constructor.prototype.classname = options.type;
    targetClasses.set(options.type, constructor);
  };
}

export function getTargetClass(type: string) : typeof Target | undefined {
  return targetClasses.get(type);
}

export class PropertyResolver<T> {
  constructor(public validator: AttributeTypes.Validator<T, Target>, public attributePath: string, public propertyPath?: string) {}

  resolve(reporter: Reporter, into: SelfBuildGraph<any>, target: Target, path: AttributePath = new AttributePath(target)) : T | undefined {
    let attr = target.attributes[this.attributePath];
    let r: T | undefined = undefined;
    path.push('.', this.attributePath);
    if (attr !== undefined || (this.propertyPath && (r = into[this.propertyPath]) === undefined)) {
      r = this.validator(reporter, path, attr, target);
      if (this.propertyPath && r !== undefined)
        into[this.propertyPath] = r;
    }
    path.pop(2);
    return r;
  }
}

function setupResolvers(prototype: { resolvers: PropertyResolver<any>[] }) : PropertyResolver<any>[] {
  let p = prototype;
  if (!p.hasOwnProperty('resolvers'))
    p.resolvers = prototype.resolvers ? prototype.resolvers.slice() : [];
  return p.resolvers;
}

function pushResolvers(prototype: { resolvers: PropertyResolver<any>[] }, r: PropertyResolver<any>[]) {
  setupResolvers(prototype).push(...r);
}

export function declareResolvers(r: PropertyResolver<any>[]) {
  return function installResolvers(cls: { prototype: { resolvers: PropertyResolver<any>[] } }) {
    pushResolvers(cls.prototype, r);
  };
}
export function resolver<T>(r: AttributeTypes.Validator<T, Target>, options?: {
  attributePath?: string
 }) {
  return function pushResolverOnProperty(prototype: typeof SelfBuildGraph.prototype, propertyName: string, descriptor?: TypedPropertyDescriptor<T>) {
    setupResolvers(prototype).push(new PropertyResolver(r, (options && options.attributePath) || propertyName, propertyName));
  };
}

export abstract class SelfBuildGraph<P extends Graph> extends Graph {
  readonly resolvers: PropertyResolver<any>[]; // on the prototype
  graph: P;

  constructor(name: TaskName, graph: P) {
    super(name, graph);
  }

  resolve(reporter: Reporter, target: Target, path: AttributePath = new AttributePath(target)) {
    for (var r of this.resolvers) {
      r.resolve(reporter, this, target, path);
    }
  }

  buildGraph(reporter: Reporter) {}
  configureExports(reporter: Reporter) {}
}
setupResolvers(SelfBuildGraph.prototype);

const configureResolver = new PropertyResolver<void>(AttributeTypes.functionValidator("(target: Target) => void"), "configure");

export class Target extends SelfBuildGraph<RootGraph> {
  name: { type: "target", name: string, environment: string, variant: string, project: string };

  dependencies: Set<Target>;
  requiredBy: Set<Target>;

  exports: TargetExportsElement;
  exportsTask: Target.GenerateExports;
  project: Project;
  attributes: BuildTargetElement;
  paths: {
    output: string,
    build: string,
    shared: string,
    intermediates: string,
    tasks: string,
  };
  modifiers: ((reporter: Reporter, task: Task) => void)[];

  variant: string;
  environment: string;
  targetName: string;

  @resolver(AttributeTypes.validateString)
  outputName: string;

  @resolver(AttributeTypes.validateString)
  outputFinalName: string | null = null;

  constructor(graph: RootGraph, project: Project, attributes: BuildTargetElement) {
    super({
      type: "target",
      name: attributes.name,
      environment: attributes.environment.name,
      variant: attributes.variant,
      project: project.path
    }, graph);

    this.project = project;
    this.variant = attributes.variant;
    this.targetName = attributes.name;
    this.outputName = attributes.name;
    this.environment = attributes.environment.name;
    this.attributes = attributes;

    this.modifiers = [];
    let env_variant = `${this.environment}/${this.variant}`;
    let build = path.join(this.project.workspace.directory, '.build', env_variant);
    this.paths = {
      output       : path.join(this.project.workspace.directory, env_variant),
      shared       : path.join(this.project.workspace.directory, '.shared', env_variant),
      build        : build,
      intermediates: path.join(build, "intermediates", this.targetName),
      tasks        : path.join(build, "tasks")
    };
    this.exports = new TargetExportsElement(this, this.attributes.name);
    this.attributes.targets.forEach(t => t.__target && this.addDependency(t.__target));
    fs.ensureDirSync(this.paths.tasks);
    fs.ensureDirSync(this.paths.intermediates);
    fs.ensureDirSync(this.paths.output);
  }

  __path() {
    return this.attributes.__path();
  }

  uniqueKey(hash: Hash) : boolean {
    hash.update(this.variant + "\t" + this.environment + "\t" + this.targetName);
    return true;
  }

  storagePath(task: Task) {
    var id = task.id();
    return id ? this.paths.tasks + '/' + id : null;
  }

  configure(reporter: Reporter, path: AttributePath) {
    this.resolve(reporter, this, path);
    configureResolver.resolve(reporter, this, this, path);
  }

  configureExports(reporter: Reporter) {
    let components = <ComponentElement[]>[];
    this.attributes.exports.forEach(c => {
      if (c.name)
        this.exports[`${c.name}=`] = c;
      if (c.is === 'component')
        components.push(c);
    });
  }
{'fraction_non_alphanumeric': 0.13414634146341464, 'fraction_numerical': 0.00017056114617090227, 'mean_word_length': 4.221282279608192, 'pattern_counts': {'":': 0, '<': 43, '<?xml version=': 0, '>': 51, 'https://': 0, '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': '1343320', 'n_tokens_mistral': 3447, 'n_tokens_neox': 3393, 'n_words': 801}