From dc45559c1723d40c886801575e2a7a0b5722dd5e Mon Sep 17 00:00:00 2001 From: Igor Minar Date: Sun, 31 May 2015 17:24:21 -0700 Subject: [PATCH] build(broccoli): add support for DiffResult#addedPaths Some plugins want to explicitly know of new paths, so we need to distinguish them from changed paths. --- tools/broccoli/broccoli-dartfmt.ts | 13 +++--- tools/broccoli/broccoli-dest-copy.ts | 13 +++--- tools/broccoli/broccoli-flatten.spec.ts | 22 ++++++++++ tools/broccoli/broccoli-flatten.ts | 20 +++++++-- tools/broccoli/broccoli-lodash.ts | 2 +- tools/broccoli/broccoli-merge-trees.spec.ts | 29 ++++++++++--- tools/broccoli/broccoli-merge-trees.ts | 17 ++++++-- tools/broccoli/broccoli-replace.ts | 47 +++++++++++---------- tools/broccoli/broccoli-ts2dart.ts | 17 ++++---- tools/broccoli/broccoli-typescript.ts | 19 +++++---- tools/broccoli/traceur/index.ts | 38 +++++++++-------- tools/broccoli/tree-differ.spec.ts | 43 ++++++++++++------- tools/broccoli/tree-differ.ts | 30 +++++++++---- 13 files changed, 204 insertions(+), 106 deletions(-) diff --git a/tools/broccoli/broccoli-dartfmt.ts b/tools/broccoli/broccoli-dartfmt.ts index 5f3a6a1169..806549c311 100644 --- a/tools/broccoli/broccoli-dartfmt.ts +++ b/tools/broccoli/broccoli-dartfmt.ts @@ -30,12 +30,13 @@ class DartFormatter implements DiffingBroccoliPlugin { rebuild(treeDiff: DiffResult): Promise { let args = ['-w']; - treeDiff.changedPaths.forEach((changedFile) => { - let sourcePath = path.join(this.inputPath, changedFile); - let destPath = path.join(this.cachePath, changedFile); - if (/\.dart$/.test(changedFile)) args.push(destPath); - fse.copySync(sourcePath, destPath); - }); + treeDiff.addedPaths.concat(treeDiff.changedPaths) + .forEach((changedFile) => { + let sourcePath = path.join(this.inputPath, changedFile); + let destPath = path.join(this.cachePath, changedFile); + if (/\.dart$/.test(changedFile)) args.push(destPath); + fse.copySync(sourcePath, destPath); + }); treeDiff.removedPaths.forEach((removedFile) => { let destPath = path.join(this.cachePath, removedFile); fse.removeSync(destPath); diff --git a/tools/broccoli/broccoli-dest-copy.ts b/tools/broccoli/broccoli-dest-copy.ts index e5abc7f4f0..ecfd90a482 100644 --- a/tools/broccoli/broccoli-dest-copy.ts +++ b/tools/broccoli/broccoli-dest-copy.ts @@ -15,13 +15,14 @@ class DestCopy implements DiffingBroccoliPlugin { rebuild(treeDiff: DiffResult) { - treeDiff.changedPaths.forEach((changedFilePath) => { - var destFilePath = path.join(this.outputRoot, changedFilePath); + treeDiff.addedPaths.concat(treeDiff.changedPaths) + .forEach((changedFilePath) => { + var destFilePath = path.join(this.outputRoot, changedFilePath); - var destDirPath = path.dirname(destFilePath); - fse.mkdirsSync(destDirPath); - fse.copySync(path.join(this.inputPath, changedFilePath), destFilePath); - }); + var destDirPath = path.dirname(destFilePath); + fse.mkdirsSync(destDirPath); + fse.copySync(path.join(this.inputPath, changedFilePath), destFilePath); + }); treeDiff.removedPaths.forEach((removedFilePath) => { var destFilePath = path.join(this.outputRoot, removedFilePath); diff --git a/tools/broccoli/broccoli-flatten.spec.ts b/tools/broccoli/broccoli-flatten.spec.ts index 5a975eafbd..f44b36a60b 100644 --- a/tools/broccoli/broccoli-flatten.spec.ts +++ b/tools/broccoli/broccoli-flatten.spec.ts @@ -52,4 +52,26 @@ describe('Flatten', () => { expect(fs.readdirSync('output')).toEqual(['file-1.1.txt', 'file-2.txt', 'file-3.txt']); }); + + + it('should throw an exception if duplicates are found', () => { + let testDir = { + 'input': { + 'dir1': { + 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), + 'subdir-1': { + 'file-1.txt': mockfs.file({content: 'file-1.1.txt content', mtime: new Date(1000)}) + }, + 'empty-dir': {} + }, + }, + 'output': {} + }; + mockfs(testDir); + + let differ = new TreeDiffer('testLabel', 'input'); + let flattenedTree = flatten('input'); + expect(() => flattenedTree.rebuild(differ.diffTree())). + toThrowError("Duplicate file 'file-1.txt' found in path 'dir1/subdir-1/file-1.txt'"); + }); }); diff --git a/tools/broccoli/broccoli-flatten.ts b/tools/broccoli/broccoli-flatten.ts index fd99aa849d..079c15f671 100644 --- a/tools/broccoli/broccoli-flatten.ts +++ b/tools/broccoli/broccoli-flatten.ts @@ -2,6 +2,9 @@ import fs = require('fs'); import fse = require('fs-extra'); import path = require('path'); import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin'; +var symlinkOrCopy = require('symlink-or-copy').sync; + +var isWindows = process.platform === 'win32'; /** @@ -11,8 +14,17 @@ import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-br export class DiffingFlatten implements DiffingBroccoliPlugin { constructor(private inputPath, private cachePath, private options) {} + rebuild(treeDiff: DiffResult) { - treeDiff.changedPaths.forEach((changedFilePath) => { + let pathsToUpdate = treeDiff.addedPaths; + + // since we need to run on Windows as well we can't rely on symlinks being available, + // which means that we need to respond to both added and changed paths + if (isWindows) { + pathsToUpdate = pathsToUpdate.concat(treeDiff.changedPaths); + } + + pathsToUpdate.forEach((changedFilePath) => { var sourceFilePath = path.join(this.inputPath, changedFilePath); var destFilePath = path.join(this.cachePath, path.basename(changedFilePath)); var destDirPath = path.dirname(destFilePath); @@ -21,9 +33,11 @@ export class DiffingFlatten implements DiffingBroccoliPlugin { fse.mkdirpSync(destDirPath); } - // TODO: once we have addedPaths support, we should throw dupes are found if (!fs.existsSync(destFilePath)) { - fs.symlinkSync(sourceFilePath, destFilePath); + symlinkOrCopy(sourceFilePath, destFilePath); + } else { + throw new Error(`Duplicate file '${path.basename(changedFilePath)}' ` + + `found in path '${changedFilePath}'`); } }); diff --git a/tools/broccoli/broccoli-lodash.ts b/tools/broccoli/broccoli-lodash.ts index 43ca661736..ce1ea3bb0d 100644 --- a/tools/broccoli/broccoli-lodash.ts +++ b/tools/broccoli/broccoli-lodash.ts @@ -37,7 +37,7 @@ export class LodashRenderer implements DiffingBroccoliPlugin { fs.unlinkSync(destFilePath); }; - treeDiff.changedPaths.forEach(processFile); + treeDiff.addedPaths.concat(treeDiff.changedPaths).forEach(processFile); treeDiff.removedPaths.forEach(removeFile); } } diff --git a/tools/broccoli/broccoli-merge-trees.spec.ts b/tools/broccoli/broccoli-merge-trees.spec.ts index 3c149f9c14..d29e640320 100644 --- a/tools/broccoli/broccoli-merge-trees.spec.ts +++ b/tools/broccoli/broccoli-merge-trees.spec.ts @@ -45,7 +45,7 @@ describe('MergeTrees', () => { expect(read('dest/foo.js')).toBe('tree2/foo.js content'); }); - it('should throw if duplicates are used by default', () => { + it('should throw if duplicates are found during the initial build', () => { 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)})}, @@ -54,15 +54,34 @@ describe('MergeTrees', () => { mockfs(testDir); let treeDiffer = MakeTreeDiffers(['tree1', 'tree2', 'tree3']); let treeMerger = mergeTrees(['tree1', 'tree2', 'tree3'], 'dest', {}); - expect(() => treeMerger.rebuild(treeDiffer.diffTrees())).toThrow(); + expect(() => treeMerger.rebuild(treeDiffer.diffTrees())). + toThrowError("`overwrite` option is required for handling duplicates."); - delete testDir.tree2['foo.js']; - delete testDir.tree3['foo.js']; + testDir = { + 'tree1': {'foo.js': mockfs.file({content: 'tree1/foo.js content', mtime: new Date(1000)})}, + 'tree2': {}, + 'tree3': {} + }; mockfs(testDir); + }); + + + it('should throw if duplicates are found during rebuild', () => { + let testDir = { + 'tree1': {'foo.js': mockfs.file({content: 'tree1/foo.js content', mtime: new Date(1000)})}, + 'tree2': {}, + 'tree3': {} + }; + mockfs(testDir); + + let treeDiffer = MakeTreeDiffers(['tree1', 'tree2', 'tree3']); + let treeMerger = mergeTrees(['tree1', 'tree2', 'tree3'], 'dest', {}); expect(() => treeMerger.rebuild(treeDiffer.diffTrees())).not.toThrow(); + testDir.tree2['foo.js'] = mockfs.file({content: 'tree2/foo.js content', mtime: new Date(1000)}); mockfs(testDir); - expect(() => treeMerger.rebuild(treeDiffer.diffTrees())).toThrow(); + expect(() => treeMerger.rebuild(treeDiffer.diffTrees())). + toThrowError("`overwrite` option is required for handling duplicates."); }); }); diff --git a/tools/broccoli/broccoli-merge-trees.ts b/tools/broccoli/broccoli-merge-trees.ts index ea66e8ef59..ac2e6043ec 100644 --- a/tools/broccoli/broccoli-merge-trees.ts +++ b/tools/broccoli/broccoli-merge-trees.ts @@ -4,6 +4,8 @@ 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; } @@ -43,10 +45,12 @@ export class MergeTrees implements DiffingBroccoliPlugin { }; if (this.firstBuild) { + this.firstBuild = false; + // Build initial cache treeDiffs.reverse().forEach((treeDiff: DiffResult, index) => { index = treeDiffs.length - 1 - index; - treeDiff.changedPaths.forEach((changedPath) => { + treeDiff.addedPaths.forEach((changedPath) => { let cache = this.pathCache[changedPath]; if (cache === undefined) { this.pathCache[changedPath] = [index]; @@ -59,7 +63,7 @@ export class MergeTrees implements DiffingBroccoliPlugin { } }); }); - this.firstBuild = false; + } else { // Update cache treeDiffs.reverse().forEach((treeDiff: DiffResult, index) => { @@ -81,7 +85,14 @@ export class MergeTrees implements DiffingBroccoliPlugin { } } }); - treeDiff.changedPaths.forEach((changedPath) => { + + 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 diff --git a/tools/broccoli/broccoli-replace.ts b/tools/broccoli/broccoli-replace.ts index 9667a4765a..b190b52d89 100644 --- a/tools/broccoli/broccoli-replace.ts +++ b/tools/broccoli/broccoli-replace.ts @@ -17,32 +17,33 @@ class DiffingReplace implements DiffingBroccoliPlugin { var patterns = this.options.patterns; var files = this.options.files; - treeDiff.changedPaths.forEach((changedFilePath) => { - var sourceFilePath = path.join(this.inputPath, changedFilePath); - var destFilePath = path.join(this.cachePath, changedFilePath); - var destDirPath = path.dirname(destFilePath); + treeDiff.addedPaths.concat(treeDiff.changedPaths) + .forEach((changedFilePath) => { + var sourceFilePath = path.join(this.inputPath, changedFilePath); + var destFilePath = path.join(this.cachePath, changedFilePath); + var destDirPath = path.dirname(destFilePath); - if (!fs.existsSync(destDirPath)) { - fse.mkdirpSync(destDirPath); - } - - var fileMatches = files.some((filePath) => minimatch(changedFilePath, filePath)); - if (fileMatches) { - var content = fs.readFileSync(sourceFilePath, FILE_ENCODING); - patterns.forEach((pattern) => { - var replacement = pattern.replacement; - if (typeof replacement === 'function') { - replacement = function(content) { - return pattern.replacement(content, changedFilePath); - }; + if (!fs.existsSync(destDirPath)) { + fse.mkdirpSync(destDirPath); + } + + var fileMatches = files.some((filePath) => minimatch(changedFilePath, filePath)); + if (fileMatches) { + var content = fs.readFileSync(sourceFilePath, FILE_ENCODING); + patterns.forEach((pattern) => { + var replacement = pattern.replacement; + if (typeof replacement === 'function') { + replacement = function(content) { + return pattern.replacement(content, changedFilePath); + }; + } + content = content.replace(pattern.match, replacement); + }); + fs.writeFileSync(destFilePath, content, FILE_ENCODING); + } else if (!fs.existsSync(destFilePath)) { + fs.symlinkSync(sourceFilePath, destFilePath); } - content = content.replace(pattern.match, replacement); }); - fs.writeFileSync(destFilePath, content, FILE_ENCODING); - } else if (!fs.existsSync(destFilePath)) { - fs.symlinkSync(sourceFilePath, destFilePath); - } - }); treeDiff.removedPaths.forEach((removedFilePath) => { var destFilePath = path.join(this.cachePath, removedFilePath); diff --git a/tools/broccoli/broccoli-ts2dart.ts b/tools/broccoli/broccoli-ts2dart.ts index 2f818a36ea..2285d200e7 100644 --- a/tools/broccoli/broccoli-ts2dart.ts +++ b/tools/broccoli/broccoli-ts2dart.ts @@ -23,16 +23,17 @@ class TSToDartTranspiler implements DiffingBroccoliPlugin { rebuild(treeDiff: DiffResult) { let toEmit = []; let getDartFilePath = (path: string) => path.replace(/((\.js)|(\.ts))$/i, '.dart'); - treeDiff.changedPaths.forEach((changedPath) => { - let inputFilePath = path.resolve(this.inputPath, changedPath); + treeDiff.addedPaths.concat(treeDiff.changedPaths) + .forEach((changedPath) => { + let inputFilePath = path.resolve(this.inputPath, changedPath); - // Ignore files which don't need to be transpiled to Dart - let dartInputFilePath = getDartFilePath(inputFilePath); - if (fs.existsSync(dartInputFilePath)) return; + // Ignore files which don't need to be transpiled to Dart + let dartInputFilePath = getDartFilePath(inputFilePath); + if (fs.existsSync(dartInputFilePath)) return; - // Prepare to rebuild - toEmit.push(path.resolve(this.inputPath, changedPath)); - }); + // Prepare to rebuild + toEmit.push(path.resolve(this.inputPath, changedPath)); + }); treeDiff.removedPaths.forEach((removedPath) => { let absolutePath = path.resolve(this.inputPath, removedPath); diff --git a/tools/broccoli/broccoli-typescript.ts b/tools/broccoli/broccoli-typescript.ts index 6e3558b3c4..7fcf04dcfc 100644 --- a/tools/broccoli/broccoli-typescript.ts +++ b/tools/broccoli/broccoli-typescript.ts @@ -50,16 +50,17 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin { let pathsToEmit = []; let pathsWithErrors = []; - treeDiff.changedPaths.forEach((tsFilePath) => { - if (!this.fileRegistry[tsFilePath]) { - this.fileRegistry[tsFilePath] = {version: 0}; - this.rootFilePaths.push(tsFilePath); - } else { - this.fileRegistry[tsFilePath].version++; - } + treeDiff.addedPaths.concat(treeDiff.changedPaths) + .forEach((tsFilePath) => { + if (!this.fileRegistry[tsFilePath]) { + this.fileRegistry[tsFilePath] = {version: 0}; + this.rootFilePaths.push(tsFilePath); + } else { + this.fileRegistry[tsFilePath].version++; + } - pathsToEmit.push(tsFilePath); - }); + pathsToEmit.push(tsFilePath); + }); treeDiff.removedPaths.forEach((tsFilePath) => { console.log('removing outputs for', tsFilePath); diff --git a/tools/broccoli/traceur/index.ts b/tools/broccoli/traceur/index.ts index f0a2bb15d9..356d22886d 100644 --- a/tools/broccoli/traceur/index.ts +++ b/tools/broccoli/traceur/index.ts @@ -16,29 +16,31 @@ class DiffingTraceurCompiler implements DiffingBroccoliPlugin { static includeExtensions = ['.js', '.es6', '.cjs']; rebuild(treeDiff: DiffResult) { - treeDiff.changedPaths.forEach((changedFilePath) => { - var traceurOpts = xtend({filename: changedFilePath}, this.options.traceurOptions); + treeDiff.addedPaths.concat(treeDiff.changedPaths) + .forEach((changedFilePath) => { + var traceurOpts = xtend({filename: changedFilePath}, this.options.traceurOptions); - var fsOpts = {encoding: 'utf-8'}; - var absoluteInputFilePath = path.join(this.inputPath, changedFilePath); - var sourcecode = fs.readFileSync(absoluteInputFilePath, fsOpts); + var fsOpts = {encoding: 'utf-8'}; + var absoluteInputFilePath = path.join(this.inputPath, changedFilePath); + var sourcecode = fs.readFileSync(absoluteInputFilePath, fsOpts); - var result = traceur.compile(traceurOpts, changedFilePath, sourcecode); + var result = traceur.compile(traceurOpts, changedFilePath, sourcecode); - // TODO: we should fix the sourceMappingURL written by Traceur instead of overriding - // (but we might switch to typescript first) - var mapFilepath = changedFilePath.replace(/\.\w+$/, '') + this.options.destSourceMapExtension; - result.js = result.js + '\n//# sourceMappingURL=./' + path.basename(mapFilepath); + // TODO: we should fix the sourceMappingURL written by Traceur instead of overriding + // (but we might switch to typescript first) + var mapFilepath = + changedFilePath.replace(/\.\w+$/, '') + this.options.destSourceMapExtension; + result.js = result.js + '\n//# sourceMappingURL=./' + path.basename(mapFilepath); - var destFilepath = changedFilePath.replace(/\.\w+$/, this.options.destExtension); - var destFile = path.join(this.cachePath, destFilepath); - fse.mkdirsSync(path.dirname(destFile)); - fs.writeFileSync(destFile, result.js, fsOpts); + var destFilepath = changedFilePath.replace(/\.\w+$/, this.options.destExtension); + var destFile = path.join(this.cachePath, destFilepath); + fse.mkdirsSync(path.dirname(destFile)); + fs.writeFileSync(destFile, result.js, fsOpts); - var destMap = path.join(this.cachePath, mapFilepath); - result.sourceMap.file = destFilepath; - fs.writeFileSync(destMap, JSON.stringify(result.sourceMap), fsOpts); - }); + var destMap = path.join(this.cachePath, mapFilepath); + result.sourceMap.file = destFilepath; + fs.writeFileSync(destMap, JSON.stringify(result.sourceMap), fsOpts); + }); treeDiff.removedPaths.forEach((removedFilePath) => { var destFilepath = removedFilePath.replace(/\.\w+$/, this.options.destExtension); diff --git a/tools/broccoli/tree-differ.spec.ts b/tools/broccoli/tree-differ.spec.ts index 1635099475..c698a1f4fe 100644 --- a/tools/broccoli/tree-differ.spec.ts +++ b/tools/broccoli/tree-differ.spec.ts @@ -11,9 +11,9 @@ describe('TreeDiffer', () => { afterEach(() => mockfs.restore()); - describe('diff of changed files', () => { + describe('diff of added and changed files', () => { - it('should list all files but no directories during the first diff', () => { + it('should list all files (but no directories) during the first diff', () => { let testDir = { 'dir1': { 'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)}), @@ -30,9 +30,10 @@ describe('TreeDiffer', () => { let diffResult = differ.diffTree(); - expect(diffResult.changedPaths) + expect(diffResult.addedPaths) .toEqual(['file-1.txt', 'file-2.txt', 'subdir-1/file-1.1.txt']); + expect(diffResult.changedPaths).toEqual([]); expect(diffResult.removedPaths).toEqual([]); }); @@ -53,11 +54,13 @@ describe('TreeDiffer', () => { let diffResult = differ.diffTree(); - expect(diffResult.changedPaths).not.toEqual([]); + expect(diffResult.addedPaths).not.toEqual([]); + expect(diffResult.changedPaths).toEqual([]); expect(diffResult.removedPaths).toEqual([]); diffResult = differ.diffTree(); + expect(diffResult.addedPaths).toEqual([]); expect(diffResult.changedPaths).toEqual([]); expect(diffResult.removedPaths).toEqual([]); }); @@ -80,7 +83,7 @@ describe('TreeDiffer', () => { let diffResult = differ.diffTree(); - expect(diffResult.changedPaths) + expect(diffResult.addedPaths) .toEqual(['file-1.txt', 'file-2.txt', 'subdir-1/file-1.1.txt']); // change two files @@ -126,7 +129,7 @@ describe('TreeDiffer', () => { let diffResult = differ.diffTree(); - expect(diffResult.changedPaths) + expect(diffResult.addedPaths) .toEqual(['file-1.txt', 'file-2.txt', 'subdir-1/file-1.1.txt']); // change two files @@ -138,8 +141,8 @@ describe('TreeDiffer', () => { diffResult = differ.diffTree(); + expect(diffResult.addedPaths).toEqual([]); expect(diffResult.changedPaths).toEqual(['file-1.txt', 'subdir-1/file-1.1.txt']); - expect(diffResult.removedPaths).toEqual([]); // change one file @@ -155,6 +158,7 @@ describe('TreeDiffer', () => { mockfs(testDir); diffResult = differ.diffTree(); + expect(diffResult.addedPaths).toEqual([]); expect(diffResult.changedPaths).toEqual([]); expect(diffResult.removedPaths).toEqual(['file-1.txt']); @@ -171,7 +175,8 @@ describe('TreeDiffer', () => { mockfs(testDir); diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual(['file-1.txt']); + expect(diffResult.addedPaths).toEqual(['file-1.txt']); + expect(diffResult.changedPaths).toEqual([]); expect(diffResult.removedPaths).toEqual([]); }); @@ -204,7 +209,9 @@ describe('TreeDiffer', () => { let diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual(['file-1.js', 'file-3.coffee']); + expect(diffResult.addedPaths).toEqual(['file-1.js', 'file-3.coffee']); + expect(diffResult.changedPaths).toEqual([]); + expect(diffResult.removedPaths).toEqual([]); // change two files testDir['dir1']['file-1.js'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); @@ -216,8 +223,8 @@ describe('TreeDiffer', () => { diffResult = differ.diffTree(); + expect(diffResult.addedPaths).toEqual([]); expect(diffResult.changedPaths).toEqual(['file-1.js', 'file-3.coffee']); - expect(diffResult.removedPaths).toEqual([]); // change one file @@ -250,7 +257,7 @@ describe('TreeDiffer', () => { let diffResult = differ.diffTree(); - expect(diffResult.changedPaths) + expect(diffResult.addedPaths) .toEqual(['file-1.cs', 'file-1.ts', 'file-1d.cs', 'file-3.ts']); // change two files @@ -265,8 +272,8 @@ describe('TreeDiffer', () => { diffResult = differ.diffTree(); + expect(diffResult.addedPaths).toEqual([]); expect(diffResult.changedPaths).toEqual(['file-1.cs', 'file-1.ts', 'file-3.ts']); - expect(diffResult.removedPaths).toEqual([]); // change one file @@ -280,7 +287,7 @@ describe('TreeDiffer', () => { describe('diff of new files', () => { - it('should detect file additions and report them as changed files', () => { + it('should detect file additions', () => { let testDir = { 'dir1': {'file-1.txt': mockfs.file({content: 'file-1.txt content', mtime: new Date(1000)})} @@ -294,7 +301,9 @@ describe('TreeDiffer', () => { mockfs(testDir); let diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual(['file-2.txt']); + expect(diffResult.addedPaths).toEqual(['file-2.txt']); + expect(diffResult.changedPaths).toEqual([]); + expect(diffResult.removedPaths).toEqual([]); }); @@ -313,7 +322,8 @@ describe('TreeDiffer', () => { mockfs(testDir); let diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual(['file-1.txt', 'file-2.txt']); + expect(diffResult.addedPaths).toEqual(['file-2.txt']); + expect(diffResult.changedPaths).toEqual(['file-1.txt']); }); }); @@ -358,7 +368,8 @@ describe('TreeDiffer', () => { mockfs(testDir); let diffResult = differ.diffTree(); - expect(diffResult.changedPaths).toEqual(['file-1.txt', 'file-3.txt']); + expect(diffResult.addedPaths).toEqual(['file-3.txt']); + expect(diffResult.changedPaths).toEqual(['file-1.txt']); expect(diffResult.removedPaths).toEqual(['file-2.txt']); }); }); diff --git a/tools/broccoli/tree-differ.ts b/tools/broccoli/tree-differ.ts index 48d25afc4a..15c35ab7b3 100644 --- a/tools/broccoli/tree-differ.ts +++ b/tools/broccoli/tree-differ.ts @@ -66,8 +66,14 @@ export class TreeDiffer { if (!(this.include && !absolutePath.match(this.include)) && !(this.exclude && absolutePath.match(this.exclude))) { result.filesChecked++; - if (this.isFileDirty(absolutePath, pathStat)) { - result.changedPaths.push(path.relative(this.rootPath, absolutePath)); + let relativeFilePath = path.relative(this.rootPath, absolutePath); + + switch (this.isFileDirty(absolutePath, pathStat)) { + case FileStatus.ADDED: + result.addedPaths.push(relativeFilePath); + break; + case FileStatus.CHANGED: + result.changedPaths.push(relativeFilePath); } } } @@ -77,7 +83,7 @@ export class TreeDiffer { } - private isFileDirty(path: string, stat: fs.Stats): boolean { + private isFileDirty(path: string, stat: fs.Stats): FileStatus { let oldFingerprint = this.fingerprints[path]; let newFingerprint = `${stat.mtime.getTime()} # ${stat.size}`; @@ -88,11 +94,13 @@ export class TreeDiffer { if (oldFingerprint === newFingerprint) { // nothing changed - return false; + return FileStatus.UNCHANGED; } + + return FileStatus.CHANGED; } - return true; + return FileStatus.ADDED; } @@ -114,6 +122,7 @@ export class TreeDiffer { export interface DiffResult { + addedPaths: string[]; changedPaths: string[]; removedPaths: string[]; log(verbose: boolean): void; @@ -124,6 +133,7 @@ export interface DiffResult { class DirtyCheckingDiffResult { public filesChecked: number = 0; public directoriesChecked: number = 0; + public addedPaths: string[] = []; public changedPaths: string[] = []; public removedPaths: string[] = []; public startTime: number = Date.now(); @@ -133,13 +143,14 @@ class DirtyCheckingDiffResult { toString() { return `${pad(this.label, 30)}, ${pad(this.endTime - this.startTime, 5)}ms, ` + - `${pad(this.changedPaths.length + this.removedPaths.length, 5)} changes ` + + `${pad(this.addedPaths.length + this.changedPaths.length + this.removedPaths.length, 5)} changes ` + `(files: ${pad(this.filesChecked, 5)}, dirs: ${pad(this.directoriesChecked, 4)})`; } log(verbose) { - let prefixedPaths = - this.changedPaths.map((p) => `* ${p}`).concat(this.removedPaths.map((p) => `- ${p}`)); + let prefixedPaths = this.addedPaths.map(p => `+ ${p}`) + .concat(this.changedPaths.map(p => `* ${p}`)) + .concat(this.removedPaths.map(p => `- ${p}`)); console.log(`Tree diff: ${this}` + ((verbose && prefixedPaths.length) ? ` [\n ${prefixedPaths.join('\n ')}\n]` : '')); @@ -153,3 +164,6 @@ function pad(value, length) { whitespaceLength = whitespaceLength + 1; return new Array(whitespaceLength).join(' ') + value; } + + +enum FileStatus { ADDED, UNCHANGED, CHANGED }