diff --git a/tools/broccoli/broccoli-tree-stabilizer.ts b/tools/broccoli/broccoli-tree-stabilizer.ts
new file mode 100644
index 0000000000..9a1bf97a4f
--- /dev/null
+++ b/tools/broccoli/broccoli-tree-stabilizer.ts
@@ -0,0 +1,45 @@
+///
+///
+
+import fs = require('fs');
+let symlinkOrCopy = require('symlink-or-copy');
+
+
+/**
+ * Stabilizes the inputPath for the following plugins in the build tree.
+ *
+ * All broccoli plugins that inherit from `broccoli-writer` or `broccoli-filter` change their
+ * outputPath during each rebuild.
+ *
+ * This means that all following plugins in the build tree can't rely on their inputPath being
+ * immutable. This results in breakage of any plugin that is not expecting such behavior.
+ *
+ * For example all `DiffingBroccoliPlugin`s expect their inputPath to be stable.
+ *
+ * By inserting this plugin into the tree after any misbehaving plugin, we can stabilize the
+ * inputPath for the following plugin in the tree and correct the surprising behavior.
+ */
+class TreeStabilizer implements BroccoliTree {
+ inputPath: string;
+ outputPath: string;
+
+
+ constructor(public inputTree: BroccoliTree) {}
+
+
+ rebuild() {
+ fs.rmdirSync(this.outputPath);
+
+ // TODO: investigate if we can use rename the directory instead to improve performance on
+ // Windows
+ symlinkOrCopy.sync(this.inputPath, this.outputPath);
+ }
+
+
+ cleanup() {}
+}
+
+
+export default function stabilizeTree(inputTree) {
+ return new TreeStabilizer(inputTree);
+}
diff --git a/tools/broccoli/diffing-broccoli-plugin.ts b/tools/broccoli/diffing-broccoli-plugin.ts
index ae133996a2..dc03289591 100644
--- a/tools/broccoli/diffing-broccoli-plugin.ts
+++ b/tools/broccoli/diffing-broccoli-plugin.ts
@@ -6,6 +6,7 @@ import fs = require('fs');
import fse = require('fs-extra');
import path = require('path');
import {TreeDiffer, DiffResult} from './tree-differ';
+import stabilizeTree from './broccoli-tree-stabilizer';
let symlinkOrCopy = require('symlink-or-copy');
@@ -51,9 +52,9 @@ class DiffingPluginWrapper implements BroccoliTree {
constructor(private pluginClass, private wrappedPluginArguments) {
if (Array.isArray(wrappedPluginArguments[0])) {
- this.inputTrees = wrappedPluginArguments[0];
+ this.inputTrees = this.stabilizeTrees(wrappedPluginArguments[0]);
} else {
- this.inputTree = wrappedPluginArguments[0];
+ this.inputTree = this.stabilizeTree(wrappedPluginArguments[0]);
}
this.description = this.pluginClass.name;
@@ -82,6 +83,13 @@ class DiffingPluginWrapper implements BroccoliTree {
}
+ cleanup() {
+ if (this.wrappedPlugin.cleanup) {
+ this.wrappedPlugin.cleanup();
+ }
+ }
+
+
private relinkOutputAndCachePaths() {
// just symlink the cache and output tree
fs.rmdirSync(this.outputPath);
@@ -101,9 +109,15 @@ class DiffingPluginWrapper implements BroccoliTree {
}
- cleanup() {
- if (this.wrappedPlugin.cleanup) {
- this.wrappedPlugin.cleanup();
- }
+ private stabilizeTrees(trees: BroccoliTree[]) {
+ return trees.map((tree) => this.stabilizeTree(tree));
+ }
+
+
+ private stabilizeTree(tree: BroccoliTree) {
+ // Ignore all DiffingPlugins as they are already stable, for others we don't know for sure
+ // so we need to stabilize them.
+ // Since it's not safe to use instanceof operator in node, we are checking the constructor.name.
+ return (tree.constructor['name'] === 'DiffingPluginWrapper') ? tree : stabilizeTree(tree);
}
}
diff --git a/tools/broccoli/trees/browser_tree.ts b/tools/broccoli/trees/browser_tree.ts
index c16eccdf88..3df98400cf 100644
--- a/tools/broccoli/trees/browser_tree.ts
+++ b/tools/broccoli/trees/browser_tree.ts
@@ -56,11 +56,6 @@ module.exports = function makeBrowserTree(options, destinationPath) {
var es6Tree = mergeTrees([traceurTree, typescriptTree]);
- // TODO(iminar): tree differ seems to have issues with trees created by mergeTrees, investigate!
- // ENOENT error is thrown while doing fs.readdirSync on inputRoot
- // in the meantime, we just do noop mv to create a new tree
- es6Tree = stew.mv(es6Tree, '');
-
// Call Traceur again to lower the ES6 build tree to ES5
var es5Tree = transpileWithTraceur(es6Tree, {
destExtension: '.js',
@@ -152,10 +147,5 @@ module.exports = function makeBrowserTree(options, destinationPath) {
var mergedTree = mergeTrees([stew.mv(es6Tree, '/es6'), stew.mv(es5Tree, '/es5')]);
- // TODO(iminar): tree differ seems to have issues with trees created by mergeTrees, investigate!
- // ENOENT error is thrown while doing fs.readdirSync on inputRoot
- // in the meantime, we just do noop mv to create a new tree
- mergedTree = stew.mv(mergedTree, '');
-
return destCopy(mergedTree, destinationPath);
};
diff --git a/tools/broccoli/trees/dart_tree.ts b/tools/broccoli/trees/dart_tree.ts
index 82c965e278..d95f16d030 100644
--- a/tools/broccoli/trees/dart_tree.ts
+++ b/tools/broccoli/trees/dart_tree.ts
@@ -144,10 +144,5 @@ module.exports = function makeDartTree(destinationPath) {
var dartTree = mergeTrees([sourceTree, getTemplatedPubspecsTree(), getDocsTree()]);
- // TODO(iminar): tree differ seems to have issues with trees created by mergeTrees, investigate!
- // ENOENT error is thrown while doing fs.readdirSync on inputRoot
- // in the meantime, we just do noop mv to create a new tree
- dartTree = stew.mv(dartTree, '');
-
return destCopy(dartTree, destinationPath);
};
diff --git a/tools/broccoli/trees/node_tree.ts b/tools/broccoli/trees/node_tree.ts
index 539be352b7..8e59acfe18 100644
--- a/tools/broccoli/trees/node_tree.ts
+++ b/tools/broccoli/trees/node_tree.ts
@@ -96,11 +96,6 @@ module.exports = function makeNodeTree(destinationPath) {
}
});
- // TODO(iminar): tree differ seems to have issues with trees created by mergeTrees, investigate!
- // ENOENT error is thrown while doing fs.readdirSync on inputRoot
- // in the meantime, we just do noop mv to create a new tree
- traceurCompatibleTsModulesTree = stew.mv(traceurCompatibleTsModulesTree, '');
-
var typescriptTree = compileWithTypescript(traceurCompatibleTsModulesTree, {
allowNonTsExtensions: false,
emitDecoratorMetadata: true,
@@ -131,12 +126,5 @@ module.exports = function makeNodeTree(destinationPath) {
}]
});
-
-
- // TODO(iminar): tree differ seems to have issues with trees created by mergeTrees, investigate!
- // ENOENT error is thrown while doing fs.readdirSync on inputRoot
- // in the meantime, we just do noop mv to create a new tree
- nodeTree = stew.mv(nodeTree, '');
-
return destCopy(nodeTree, destinationPath);
};