feat(broccoli): add diffing MergeTrees plugin
Closes #1815 Closes #2064
This commit is contained in:
parent
41ae8e76f0
commit
4ee3fdaf7f
|
@ -0,0 +1,47 @@
|
|||
/// <reference path="../typings/node/node.d.ts" />
|
||||
/// <reference path="../typings/jasmine/jasmine.d.ts" />
|
||||
|
||||
let mockfs = require('mock-fs');
|
||||
import fs = require('fs');
|
||||
import {TreeDiffer} from './tree-differ';
|
||||
import {MergeTrees} from './broccoli-merge-trees';
|
||||
|
||||
describe('MergeTrees', () => {
|
||||
afterEach(() => mockfs.restore());
|
||||
|
||||
function mergeTrees(inputPaths, cachePath, options) {
|
||||
return new MergeTrees(inputPaths, cachePath, options);
|
||||
}
|
||||
|
||||
function MakeTreeDiffers(rootDirs) {
|
||||
let treeDiffers = rootDirs.map((rootDir) => new TreeDiffer('MergeTrees', rootDir));
|
||||
treeDiffers.diffTrees = () => { return treeDiffers.map(tree => tree.diffTree()); };
|
||||
return treeDiffers;
|
||||
}
|
||||
|
||||
function read(path) { return fs.readFileSync(path, "utf-8"); }
|
||||
|
||||
it('should copy the file from the right-most inputTree', () => {
|
||||
let testDir: any = {
|
||||
'tree1': {'foo.js': mockfs.file({content: 'tree1/foo.js content', mtime: new Date(1000)})},
|
||||
'tree2': {'foo.js': mockfs.file({content: 'tree2/foo.js content', mtime: new Date(1000)})},
|
||||
'tree3': {'foo.js': mockfs.file({content: 'tree3/foo.js content', mtime: new Date(1000)})}
|
||||
};
|
||||
mockfs(testDir);
|
||||
let treeDiffer = MakeTreeDiffers(['tree1', 'tree2', 'tree3']);
|
||||
let treeMerger = mergeTrees(['tree1', 'tree2', 'tree3'], 'dest', {});
|
||||
treeMerger.rebuild(treeDiffer.diffTrees());
|
||||
expect(read('dest/foo.js')).toBe('tree3/foo.js content');
|
||||
|
||||
delete testDir.tree2['foo.js'];
|
||||
delete testDir.tree3['foo.js'];
|
||||
mockfs(testDir);
|
||||
treeMerger.rebuild(treeDiffer.diffTrees());
|
||||
expect(read('dest/foo.js')).toBe('tree1/foo.js content');
|
||||
|
||||
testDir.tree2['foo.js'] = mockfs.file({content: 'tree2/foo.js content', mtime: new Date(1000)});
|
||||
mockfs(testDir);
|
||||
treeMerger.rebuild(treeDiffer.diffTrees());
|
||||
expect(read('dest/foo.js')).toBe('tree2/foo.js content');
|
||||
});
|
||||
});
|
|
@ -0,0 +1,86 @@
|
|||
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';
|
||||
|
||||
function pathExists(filePath) {
|
||||
try {
|
||||
if (fs.statSync(filePath)) {
|
||||
return true;
|
||||
}
|
||||
} catch (e) {
|
||||
if (e.code !== "ENOENT") {
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function outputFileSync(sourcePath, destPath) {
|
||||
let dirname = path.dirname(destPath);
|
||||
fse.mkdirsSync(dirname, {fs: fs});
|
||||
fse.removeSync(destPath);
|
||||
symlinkOrCopySync(sourcePath, destPath);
|
||||
}
|
||||
|
||||
export class MergeTrees implements DiffingBroccoliPlugin {
|
||||
private mergedPaths: {[key: string]: number} = Object.create(null);
|
||||
|
||||
constructor(public inputPaths: string[], public cachePath: string, public options) {}
|
||||
|
||||
rebuild(treeDiffs: DiffResult[]) {
|
||||
treeDiffs.forEach((treeDiff: DiffResult, index) => {
|
||||
let inputPath = this.inputPaths[index];
|
||||
let existsLater = (relativePath) => {
|
||||
for (let i = treeDiffs.length - 1; i > index; --i) {
|
||||
if (pathExists(path.join(this.inputPaths[i], relativePath))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
};
|
||||
let existsSooner = (relativePath) => {
|
||||
for (let i = index - 1; i >= 0; --i) {
|
||||
if (pathExists(path.join(this.inputPaths[i], relativePath))) {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
};
|
||||
treeDiff.changedPaths.forEach((changedPath) => {
|
||||
let inputTreeIndex = this.mergedPaths[changedPath];
|
||||
if (inputTreeIndex !== index && !existsLater(changedPath)) {
|
||||
inputTreeIndex = this.mergedPaths[changedPath] = index;
|
||||
let sourcePath = path.join(inputPath, changedPath);
|
||||
let destPath = path.join(this.cachePath, changedPath);
|
||||
outputFileSync(sourcePath, destPath);
|
||||
}
|
||||
});
|
||||
|
||||
treeDiff.removedPaths.forEach((removedPath) => {
|
||||
let inputTreeIndex = this.mergedPaths[removedPath];
|
||||
|
||||
// if inputTreeIndex !== index, this same file was handled during
|
||||
// changedPaths handling
|
||||
if (inputTreeIndex !== index) return;
|
||||
|
||||
let destPath = path.join(this.cachePath, removedPath);
|
||||
fse.removeSync(destPath);
|
||||
let newInputTreeIndex = existsSooner(removedPath);
|
||||
|
||||
// Update cached value (to either newInputTreeIndex value or undefined)
|
||||
this.mergedPaths[removedPath] = newInputTreeIndex;
|
||||
|
||||
if (newInputTreeIndex >= 0) {
|
||||
// Copy the file from the newInputTreeIndex inputPath if necessary.
|
||||
let newInputPath = this.inputPaths[newInputTreeIndex];
|
||||
let sourcePath = path.join(newInputPath, removedPath);
|
||||
outputFileSync(sourcePath, destPath);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
export default wrapDiffingPlugin(MergeTrees);
|
|
@ -3,7 +3,7 @@
|
|||
var Funnel = require('broccoli-funnel');
|
||||
var flatten = require('broccoli-flatten');
|
||||
var htmlReplace = require('../html-replace');
|
||||
var mergeTrees = require('broccoli-merge-trees');
|
||||
import mergeTrees from '../broccoli-merge-trees';
|
||||
var path = require('path');
|
||||
var replace = require('broccoli-replace');
|
||||
var stew = require('broccoli-stew');
|
||||
|
|
|
@ -6,7 +6,7 @@ import {MultiCopy} from './../multi_copy';
|
|||
import destCopy from '../broccoli-dest-copy';
|
||||
var Funnel = require('broccoli-funnel');
|
||||
var glob = require('glob');
|
||||
var mergeTrees = require('broccoli-merge-trees');
|
||||
import mergeTrees from '../broccoli-merge-trees';
|
||||
var path = require('path');
|
||||
var renderLodashTemplate = require('broccoli-lodash');
|
||||
var replace = require('broccoli-replace');
|
||||
|
|
|
@ -4,7 +4,7 @@ import destCopy from '../broccoli-dest-copy';
|
|||
import compileWithTypescript from '../broccoli-typescript';
|
||||
import transpileWithTraceur from '../traceur/index';
|
||||
var Funnel = require('broccoli-funnel');
|
||||
var mergeTrees = require('broccoli-merge-trees');
|
||||
import mergeTrees from '../broccoli-merge-trees';
|
||||
var path = require('path');
|
||||
var renderLodashTemplate = require('broccoli-lodash');
|
||||
var replace = require('broccoli-replace');
|
||||
|
|
Loading…
Reference in New Issue