All files / src/utils/angular change.ts

51.11% Statements 23/45
0% Branches 0/4
27.27% Functions 3/11
42.1% Lines 16/38

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124                                                            6x                       6x       109x 109x     109x 109x                                     6x       4x 4x     4x 4x                                 6x       7x 7x     7x 7x                                    
/**
 * @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
 */
export interface Host {
  write(path: string, content: string): Promise<void>;
  read(path: string): Promise<string>;
}
 
export interface Change {
  apply(host: Host): Promise<void>;
 
  // The file this change should be applied to. Some changes might not apply to
  // a file (maybe the config).
  readonly path: string | null;
 
  // The order this change should be applied. Normally the position inside the file.
  // Changes are applied from the bottom of a file to the top.
  readonly order: number;
 
  // The description of this change. This will be outputted in a dry or verbose run.
  readonly description: string;
}
 
/**
 * An operation that does nothing.
 */
export class NoopChange implements Change {
  description = 'No operation.';
  order = Infinity;
  path = null;
  apply() {
    return Promise.resolve();
  }
}
 
/**
 * Will add text to the source code.
 */
export class InsertChange implements Change {
  order: number;
  description: string;
 
  constructor(public path: string, public pos: number, public toAdd: string) {
    Iif (pos < 0) {
      throw new Error('Negative positions are invalid');
    }
    this.description = `Inserted ${toAdd} into position ${pos} of ${path}`;
    this.order = pos;
  }
 
  /**
   * This method does not insert spaces if there is none in the original string.
   */
  apply(host: Host) {
    return host.read(this.path).then((content) => {
      const prefix = content.substring(0, this.pos);
      const suffix = content.substring(this.pos);
 
      return host.write(this.path, `${prefix}${this.toAdd}${suffix}`);
    });
  }
}
 
/**
 * Will remove text from the source code.
 */
export class RemoveChange implements Change {
  order: number;
  description: string;
 
  constructor(public path: string, private pos: number, private toRemove: string) {
    Iif (pos < 0) {
      throw new Error('Negative positions are invalid');
    }
    this.description = `Removed ${toRemove} into position ${pos} of ${path}`;
    this.order = pos;
  }
 
  apply(host: Host): Promise<void> {
    return host.read(this.path).then((content) => {
      const prefix = content.substring(0, this.pos);
      const suffix = content.substring(this.pos + this.toRemove.length);
 
      // TODO: throw error if toRemove doesn't match removed string.
      return host.write(this.path, `${prefix}${suffix}`);
    });
  }
}
 
/**
 * Will replace text from the source code.
 */
export class ReplaceChange implements Change {
  order: number;
  description: string;
 
  constructor(public path: string, private pos: number, private oldText: string, private newText: string) {
    Iif (pos < 0) {
      throw new Error('Negative positions are invalid');
    }
    this.description = `Replaced ${oldText} into position ${pos} of ${path} with ${newText}`;
    this.order = pos;
  }
 
  apply(host: Host): Promise<void> {
    return host.read(this.path).then((content) => {
      const prefix = content.substring(0, this.pos);
      const suffix = content.substring(this.pos + this.oldText.length);
      const text = content.substring(this.pos, this.pos + this.oldText.length);
 
      Iif (text !== this.oldText) {
        return Promise.reject(new Error(`Invalid replace: "${text}" != "${this.oldText}".`));
      }
 
      // TODO: throw error if oldText doesn't match removed string.
      return host.write(this.path, `${prefix}${this.newText}${suffix}`);
    });
  }
}