BREAKING CHANGES: - host actions don't take an expression as value any more but only a method name, and assumes to get an array via the EventEmitter with the method arguments. - Renderer.setElementProperty does not take `style.`/... prefixes any more. Use the new methods `Renderer.setElementAttribute`, ... instead Part of #2476 Closes #2637
		
			
				
	
	
		
			138 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
			
		
		
	
	
			138 lines
		
	
	
		
			4.5 KiB
		
	
	
	
		
			TypeScript
		
	
	
	
	
	
import fs = require('fs');
 | 
						|
import fse = require('fs-extra');
 | 
						|
import path = require('path');
 | 
						|
var symlinkOrCopySync = require('symlink-or-copy').sync;
 | 
						|
import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin';
 | 
						|
 | 
						|
var isWindows = process.platform === 'win32';
 | 
						|
 | 
						|
interface MergeTreesOptions {
 | 
						|
  overwrite?: boolean;
 | 
						|
}
 | 
						|
 | 
						|
function outputFileSync(sourcePath, destPath) {
 | 
						|
  let dirname = path.dirname(destPath);
 | 
						|
  fse.mkdirsSync(dirname, {fs: fs});
 | 
						|
  symlinkOrCopySync(sourcePath, destPath);
 | 
						|
}
 | 
						|
 | 
						|
function pathOverwrittenError(path) {
 | 
						|
  const msg = 'Either remove the duplicate or enable the "overwrite" option for this merge.';
 | 
						|
  return new Error(`Duplicate path found while merging trees. Path: "${path}".\n${msg}`);
 | 
						|
}
 | 
						|
 | 
						|
export class MergeTrees implements DiffingBroccoliPlugin {
 | 
						|
  private pathCache: {[key: string]: number[]} = Object.create(null);
 | 
						|
  public options: MergeTreesOptions;
 | 
						|
  private firstBuild: boolean = true;
 | 
						|
 | 
						|
  constructor(public inputPaths: string[], public cachePath: string,
 | 
						|
              options: MergeTreesOptions = {}) {
 | 
						|
    this.options = options || {};
 | 
						|
  }
 | 
						|
 | 
						|
  rebuild(treeDiffs: DiffResult[]) {
 | 
						|
    let overwrite = this.options.overwrite;
 | 
						|
    let pathsToEmit: string[] = [];
 | 
						|
    let pathsToRemove: string[] = [];
 | 
						|
    let emitted: {[key: string]: boolean} = Object.create(null);
 | 
						|
    let contains = (cache, val) => {
 | 
						|
      for (let i = 0, ii = cache.length; i < ii; ++i) {
 | 
						|
        if (cache[i] === val) return true;
 | 
						|
      }
 | 
						|
      return false;
 | 
						|
    };
 | 
						|
 | 
						|
    let emit = (relativePath) => {
 | 
						|
      // ASSERT(!emitted[relativePath]);
 | 
						|
      pathsToEmit.push(relativePath);
 | 
						|
      emitted[relativePath] = true;
 | 
						|
    };
 | 
						|
 | 
						|
    if (this.firstBuild) {
 | 
						|
      this.firstBuild = false;
 | 
						|
 | 
						|
      // Build initial cache
 | 
						|
      treeDiffs.reverse().forEach((treeDiff: DiffResult, index) => {
 | 
						|
        index = treeDiffs.length - 1 - index;
 | 
						|
        treeDiff.addedPaths.forEach((changedPath) => {
 | 
						|
          let cache = this.pathCache[changedPath];
 | 
						|
          if (cache === undefined) {
 | 
						|
            this.pathCache[changedPath] = [index];
 | 
						|
            pathsToEmit.push(changedPath);
 | 
						|
          } else if (overwrite) {
 | 
						|
            // ASSERT(contains(pathsToEmit, changedPath));
 | 
						|
            cache.unshift(index);
 | 
						|
          } else {
 | 
						|
            throw pathOverwrittenError(changedPath);
 | 
						|
          }
 | 
						|
        });
 | 
						|
      });
 | 
						|
 | 
						|
    } else {
 | 
						|
      // Update cache
 | 
						|
      treeDiffs.reverse().forEach((treeDiff: DiffResult, index) => {
 | 
						|
        index = treeDiffs.length - 1 - index;
 | 
						|
        if (treeDiff.removedPaths) {
 | 
						|
          treeDiff.removedPaths.forEach((removedPath) => {
 | 
						|
            let cache = this.pathCache[removedPath];
 | 
						|
            // ASSERT(cache !== undefined);
 | 
						|
            // ASSERT(contains(cache, index));
 | 
						|
            if (cache[cache.length - 1] === index) {
 | 
						|
              pathsToRemove.push(path.join(this.cachePath, removedPath));
 | 
						|
              cache.pop();
 | 
						|
              if (cache.length === 0) {
 | 
						|
                this.pathCache[removedPath] = undefined;
 | 
						|
              } else if (!emitted[removedPath]) {
 | 
						|
                if (cache.length === 1 && !overwrite) {
 | 
						|
                  throw pathOverwrittenError(removedPath);
 | 
						|
                }
 | 
						|
                emit(removedPath);
 | 
						|
              }
 | 
						|
            }
 | 
						|
          });
 | 
						|
        }
 | 
						|
 | 
						|
        let pathsToUpdate = treeDiff.addedPaths;
 | 
						|
 | 
						|
        if (isWindows) {
 | 
						|
          pathsToUpdate = pathsToUpdate.concat(treeDiff.changedPaths);
 | 
						|
        }
 | 
						|
 | 
						|
        pathsToUpdate.forEach((changedPath) => {
 | 
						|
          let cache = this.pathCache[changedPath];
 | 
						|
          if (cache === undefined) {
 | 
						|
            // File was added
 | 
						|
            this.pathCache[changedPath] = [index];
 | 
						|
            emit(changedPath);
 | 
						|
          } else if (!contains(cache, index)) {
 | 
						|
            cache.push(index);
 | 
						|
            cache.sort((a, b) => a - b);
 | 
						|
            if (cache.length > 1 && !overwrite) {
 | 
						|
              throw pathOverwrittenError(changedPath);
 | 
						|
            }
 | 
						|
            if (cache[cache.length - 1] === index && !emitted[changedPath]) {
 | 
						|
              emit(changedPath);
 | 
						|
            }
 | 
						|
          }
 | 
						|
        });
 | 
						|
      });
 | 
						|
    }
 | 
						|
 | 
						|
    pathsToRemove.forEach((destPath) => fse.removeSync(destPath));
 | 
						|
    pathsToEmit.forEach((emittedPath) => {
 | 
						|
      let cache = this.pathCache[emittedPath];
 | 
						|
      let destPath = path.join(this.cachePath, emittedPath);
 | 
						|
      let sourceIndex = cache[cache.length - 1];
 | 
						|
      let sourceInputPath = this.inputPaths[sourceIndex];
 | 
						|
      let sourcePath = path.join(sourceInputPath, emittedPath);
 | 
						|
      if (cache.length > 1) {
 | 
						|
        fse.removeSync(destPath);
 | 
						|
      }
 | 
						|
      outputFileSync(sourcePath, destPath);
 | 
						|
    });
 | 
						|
  }
 | 
						|
}
 | 
						|
 | 
						|
export default wrapDiffingPlugin(MergeTrees);
 |