From aaf3edd1313b8a7a45b8c6cc0260c7140d000b18 Mon Sep 17 00:00:00 2001 From: Caitlin Potter Date: Wed, 6 May 2015 19:24:10 -0400 Subject: [PATCH] build(brocolli): move filename filtering into DiffingPluginWrapper Closes #1719 --- tools/broccoli/broccoli-typescript.ts | 8 +- tools/broccoli/diffing-broccoli-plugin.ts | 5 +- tools/broccoli/traceur/index.ts | 46 +++++------- tools/broccoli/tree-differ.spec.ts | 89 +++++++++++++++++++++++ tools/broccoli/tree-differ.ts | 44 ++++++++--- 5 files changed, 149 insertions(+), 43 deletions(-) diff --git a/tools/broccoli/broccoli-typescript.ts b/tools/broccoli/broccoli-typescript.ts index 86e131662b..8410e4b5bd 100644 --- a/tools/broccoli/broccoli-typescript.ts +++ b/tools/broccoli/broccoli-typescript.ts @@ -30,6 +30,8 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin { private tsServiceHost: ts.LanguageServiceHost; private tsService: ts.LanguageService; + static includeExtensions = ['.ts']; + static excludeExtensions = ['.d.ts']; constructor(public inputPath: string, public cachePath: string, public options) { this.tsOpts = Object.create(options); @@ -46,8 +48,7 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin { let pathsToEmit = []; let pathsWithErrors = []; - treeDiff.changedPaths.filter((changedPath) => - changedPath.match(/\.ts/) && !changedPath.match(/\.d\.ts/)) + treeDiff.changedPaths .forEach((tsFilePath) => { if (!this.fileRegistry[tsFilePath]) { this.fileRegistry[tsFilePath] = {version: 0}; @@ -59,8 +60,7 @@ class DiffingTSCompiler implements DiffingBroccoliPlugin { pathsToEmit.push(tsFilePath); }); - treeDiff.removedPaths.filter((changedPath) => - changedPath.match(/\.ts/) && !changedPath.match(/\.d\.ts/)) + treeDiff.removedPaths .forEach((tsFilePath) => { console.log('removing outputs for', tsFilePath); diff --git a/tools/broccoli/diffing-broccoli-plugin.ts b/tools/broccoli/diffing-broccoli-plugin.ts index 4d91cb85ac..ae133996a2 100644 --- a/tools/broccoli/diffing-broccoli-plugin.ts +++ b/tools/broccoli/diffing-broccoli-plugin.ts @@ -49,7 +49,6 @@ class DiffingPluginWrapper implements BroccoliTree { cachePath = null; outputPath = null; - constructor(private pluginClass, private wrappedPluginArguments) { if (Array.isArray(wrappedPluginArguments[0])) { this.inputTrees = wrappedPluginArguments[0]; @@ -92,8 +91,10 @@ class DiffingPluginWrapper implements BroccoliTree { private init() { if (!this.initialized) { + let includeExtensions = this.pluginClass.includeExtensions || []; + let excludeExtensions = this.pluginClass.excludeExtensions || []; this.initialized = true; - this.treeDiffer = new TreeDiffer(this.inputPath); + this.treeDiffer = new TreeDiffer(this.inputPath, includeExtensions, excludeExtensions); this.wrappedPlugin = new this.pluginClass(this.inputPath, this.cachePath, this.wrappedPluginArguments[1]); } diff --git a/tools/broccoli/traceur/index.ts b/tools/broccoli/traceur/index.ts index 76a598cac8..f0a2bb15d9 100644 --- a/tools/broccoli/traceur/index.ts +++ b/tools/broccoli/traceur/index.ts @@ -13,43 +13,37 @@ let xtend = require('xtend'); class DiffingTraceurCompiler implements DiffingBroccoliPlugin { constructor(public inputPath: string, public cachePath: string, public options) {} + static includeExtensions = ['.js', '.es6', '.cjs']; rebuild(treeDiff: DiffResult) { treeDiff.changedPaths.forEach((changedFilePath) => { - var extension = path.extname(changedFilePath).toLowerCase(); - if (extension === '.js' || extension === '.es6' || extension === '.cjs') { - var traceurOpts = xtend({filename: changedFilePath}, this.options.traceurOptions); + 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 extension = path.extname(removedFilePath).toLowerCase(); - if (extension === '.js' || extension === '.es6' || extension === '.cjs') { - var destFilepath = removedFilePath.replace(/\.\w+$/, this.options.destExtension); - var absoluteOuputFilePath = path.join(this.cachePath, destFilepath); - fs.unlinkSync(absoluteOuputFilePath); - } + var destFilepath = removedFilePath.replace(/\.\w+$/, this.options.destExtension); + var absoluteOuputFilePath = path.join(this.cachePath, destFilepath); + fs.unlinkSync(absoluteOuputFilePath); }); } } diff --git a/tools/broccoli/tree-differ.spec.ts b/tools/broccoli/tree-differ.spec.ts index beaf37000e..9ed9245d10 100644 --- a/tools/broccoli/tree-differ.spec.ts +++ b/tools/broccoli/tree-differ.spec.ts @@ -102,6 +102,95 @@ describe('TreeDiffer', () => { diffResult = differ.diffTree(); expect(diffResult.changedPaths).toEqual(['file-1.txt']); }); + + + it('should ignore files with extensions not listed in includeExtensions', () => { + let testDir = { + 'dir1': { + 'file-1.js': mockfs.file({content: 'file-1.js content', mtime: new Date(1000)}), + 'file-2.md': mockfs.file({content: 'file-2.md content', mtime: new Date(1000)}), + 'file-3.coffee': mockfs.file({content: 'file-3.coffee content', mtime: new Date(1000)}), + 'subdir-1': { + 'file-1.1.cc': mockfs.file({content: 'file-1.1.cc content', mtime: new Date(1000)}) + } + } + }; + mockfs(testDir); + + let differ = new TreeDiffer('dir1', ['.js', '.coffee']); + + let diffResult = differ.diffTree(); + + expect(diffResult.changedPaths).toEqual(['file-1.js', 'file-3.coffee']); + + // change two files + testDir['dir1']['file-1.js'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); + testDir['dir1']['file-3.coffee'] = + mockfs.file({content: 'new content', mtime: new Date(1000)}); + testDir['dir1']['subdir-1']['file-1.1.cc'] = + mockfs.file({content: 'file-1.1.cc content', mtime: new Date(9999)}); + mockfs(testDir); + + diffResult = differ.diffTree(); + + expect(diffResult.changedPaths).toEqual(['file-1.js', 'file-3.coffee']); + + expect(diffResult.removedPaths).toEqual([]); + + // change one file + testDir['dir1']['file-1.js'] = mockfs.file({content: 'super new', mtime: new Date(1000)}); + mockfs(testDir); + + diffResult = differ.diffTree(); + expect(diffResult.changedPaths).toEqual(['file-1.js']); + }); + + + it('should ignore files with extensions listed in excludeExtensions', () => { + let testDir = { + 'dir1': { + 'file-1.ts': mockfs.file({content: 'file-1.ts content', mtime: new Date(1000)}), + 'file-1.cs': mockfs.file({content: 'file-1.cs content', mtime: new Date(1000)}), + 'file-1.d.cs': mockfs.file({content: 'file-1.d.cs content', mtime: new Date(1000)}), + 'file-2.md': mockfs.file({content: 'file-2.md content', mtime: new Date(1000)}), + 'file-3.ts': mockfs.file({content: 'file-3.ts content', mtime: new Date(1000)}), + 'file-4.d.ts': mockfs.file({content: 'file-4.d.ts content', mtime: new Date(1000)}), + 'subdir-1': { + 'file-1.1.cc': mockfs.file({content: 'file-1.1.cc content', mtime: new Date(1000)}) + } + } + }; + mockfs(testDir); + + let differ = new TreeDiffer('dir1', ['.ts', '.cs'], ['.d.ts', '.d.cs']); + + let diffResult = differ.diffTree(); + + expect(diffResult.changedPaths).toEqual(['file-1.cs', 'file-1.ts', 'file-3.ts']); + + // change two files + testDir['dir1']['file-1.ts'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); + testDir['dir1']['file-1.cs'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); + testDir['dir1']['file-1.d.cs'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); + testDir['dir1']['file-3.ts'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); + testDir['dir1']['file-4.d.ts'] = mockfs.file({content: 'new content', mtime: new Date(1000)}); + testDir['dir1']['subdir-1']['file-1.1.cc'] = + mockfs.file({content: 'file-1.1.cc content', mtime: new Date(9999)}); + mockfs(testDir); + + diffResult = differ.diffTree(); + + expect(diffResult.changedPaths).toEqual(['file-1.cs', 'file-1.ts', 'file-3.ts']); + + expect(diffResult.removedPaths).toEqual([]); + + // change one file + testDir['dir1']['file-4.d.ts'] = mockfs.file({content: 'super new', mtime: new Date(1000)}); + mockfs(testDir); + + diffResult = differ.diffTree(); + expect(diffResult.changedPaths).toEqual([]); + }); }); describe('diff of new files', () => { diff --git a/tools/broccoli/tree-differ.ts b/tools/broccoli/tree-differ.ts index 552723ce79..2fbfd83d73 100644 --- a/tools/broccoli/tree-differ.ts +++ b/tools/broccoli/tree-differ.ts @@ -8,8 +8,24 @@ export class TreeDiffer { private fingerprints: {[key: string]: string} = Object.create(null); private nextFingerprints: {[key: string]: string} = Object.create(null); private rootDirName: string; + private include: RegExp = null; + private exclude: RegExp = null; - constructor(private rootPath: string) { this.rootDirName = path.basename(rootPath); } + constructor(private rootPath: string, includeExtensions?: string[], + excludeExtensions?: string[]) { + this.rootDirName = path.basename(rootPath); + + let buildRegexp = (arr) => new RegExp(`(${arr.reduce(combine, "")})$`, "i"); + + this.include = (includeExtensions || []).length ? buildRegexp(includeExtensions) : null; + this.exclude = (excludeExtensions || []).length ? buildRegexp(excludeExtensions) : null; + + function combine(prev, curr) { + if (curr.charAt(0) !== ".") throw new TypeError("Extension must begin with '.'"); + curr = '(' + curr + ')'; + return prev ? (prev + '|' + curr) : curr; + } + } public diffTree(): DiffResult { @@ -30,9 +46,12 @@ export class TreeDiffer { result.directoriesChecked++; this.dirtyCheckPath(absolutePath, result); } else { - result.filesChecked++; - if (this.isFileDirty(absolutePath, pathStat)) { - result.changedPaths.push(path.relative(this.rootPath, absolutePath)); + 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)); + } } } }); @@ -62,9 +81,12 @@ export class TreeDiffer { private detectDeletionsAndUpdateFingerprints(result: DiffResult) { for (let absolutePath in this.fingerprints) { - if (this.fingerprints[absolutePath] !== null) { - let relativePath = path.relative(this.rootPath, absolutePath); - result.removedPaths.push(relativePath); + if (!(this.include && !absolutePath.match(this.include)) && + !(this.exclude && absolutePath.match(this.exclude))) { + if (this.fingerprints[absolutePath] !== null) { + let relativePath = path.relative(this.rootPath, absolutePath); + result.removedPaths.push(relativePath); + } } } @@ -93,8 +115,7 @@ class DirtyCheckingDiffResult { constructor(public name: string) {} toString() { - return `${pad(this.name, 40)}, ` + - `duration: ${pad(this.endTime - this.startTime, 5)}ms, ` + + return `${pad(this.name, 40)}, duration: ${pad(this.endTime - this.startTime, 5)}ms, ` + `${pad(this.changedPaths.length + this.removedPaths.length, 5)} changes detected ` + `(files: ${pad(this.filesChecked, 5)}, directories: ${pad(this.directoriesChecked, 4)})`; } @@ -102,8 +123,9 @@ class DirtyCheckingDiffResult { log(verbose) { let prefixedPaths = this.changedPaths.map((p) => `* ${p}`).concat(this.removedPaths.map((p) => `- ${p}`)); - console.log(`Tree diff: ${this}` + - ((verbose && prefixedPaths.length) ? ` [\n ${prefixedPaths.join('\n ')}\n]` : '')); + console.log(`Tree diff: ${this}` + ((verbose && prefixedPaths.length) ? + ` [\n ${prefixedPaths.join('\n ')}\n]` : + '')); } }