Skip to content

Commit 8c15cce

Browse files
committed
build(broccoli): add DiffingBroccoliPlugin and refactor existing plugins to use it
tree-differ: - export both TreeDiffer and DiffResult interface diffing-broccoli-plugin: - factory class for wrapping DiffingBroccoliPlugins and turning them into BroccoliTrees broccoli-dest-copy: - refactor into DiffingBroccoliPlugin broccoli-traceur: - refactor into DiffingBroccoliPlugin
1 parent e966869 commit 8c15cce

File tree

8 files changed

+180
-125
lines changed

8 files changed

+180
-125
lines changed
Lines changed: 8 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -1,63 +1,35 @@
1-
/// <reference path="./broccoli.d.ts" />
21
/// <reference path="../typings/node/node.d.ts" />
32
/// <reference path="../typings/fs-extra/fs-extra.d.ts" />
43

54
import fs = require('fs');
65
import fse = require('fs-extra');
76
import path = require('path');
8-
import TreeDiffer = require('./tree-differ');
7+
import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from './diffing-broccoli-plugin';
98

109
/**
1110
* Intercepts each file as it is copied to the destination tempdir,
1211
* and tees a copy to the given path outside the tmp dir.
1312
*/
14-
export = function destCopy(inputTree, outputRoot) { return new DestCopy(inputTree, outputRoot); }
13+
class DestCopy implements DiffingBroccoliPlugin {
14+
constructor(private inputPath, private cachePath, private outputRoot: string) {}
1515

1616

17-
class DestCopy implements BroccoliTree {
18-
treeDirtyChecker: TreeDiffer;
19-
initialized = false;
20-
21-
// props monkey-patched by broccoli builder:
22-
inputPath = null;
23-
cachePath = null;
24-
outputPath = null;
25-
26-
27-
constructor(public inputTree: BroccoliTree, public outputRoot: string) {}
28-
29-
30-
rebuild() {
31-
let firstRun = !this.initialized;
32-
this.init();
33-
34-
let diffResult = this.treeDirtyChecker.diffTree();
35-
diffResult.log(!firstRun);
36-
37-
diffResult.changedPaths.forEach((changedFilePath) => {
17+
rebuild(treeDiff: DiffResult) {
18+
treeDiff.changedPaths.forEach((changedFilePath) => {
3819
var destFilePath = path.join(this.outputRoot, changedFilePath);
3920

4021
var destDirPath = path.dirname(destFilePath);
4122
fse.mkdirsSync(destDirPath);
4223
fse.copySync(path.join(this.inputPath, changedFilePath), destFilePath);
4324
});
4425

45-
diffResult.removedPaths.forEach((removedFilePath) => {
26+
treeDiff.removedPaths.forEach((removedFilePath) => {
4627
var destFilePath = path.join(this.outputRoot, removedFilePath);
4728

4829
// TODO: what about obsolete directories? we are not cleaning those up yet
4930
fs.unlinkSync(destFilePath);
5031
});
5132
}
52-
53-
54-
private init() {
55-
if (!this.initialized) {
56-
this.initialized = true;
57-
this.treeDirtyChecker = new TreeDiffer(this.inputPath);
58-
}
59-
}
60-
61-
62-
cleanup() {}
6333
}
34+
35+
export default wrapDiffingPlugin(DestCopy);
Lines changed: 103 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
/// <reference path="broccoli.d.ts" />
2+
/// <reference path="../typings/fs-extra/fs-extra.d.ts" />
3+
/// <reference path="../typings/node/node.d.ts" />
4+
5+
import fs = require('fs');
6+
import fse = require('fs-extra');
7+
import path = require('path');
8+
import {TreeDiffer, DiffResult} from './tree-differ';
9+
let symlinkOrCopy = require('symlink-or-copy');
10+
11+
12+
export {DiffResult} from './tree-differ';
13+
14+
15+
/**
16+
* Makes writing diffing plugins easy.
17+
*
18+
* Factory method that takes a class that implements the DiffingBroccoliPlugin interface and returns
19+
* an instance of BroccoliTree.
20+
*
21+
* @param pluginClass
22+
* @returns {DiffingPlugin}
23+
*/
24+
export function wrapDiffingPlugin(pluginClass): DiffingPluginWrapperFactory {
25+
return function() { return new DiffingPluginWrapper(pluginClass, arguments); };
26+
}
27+
28+
29+
export interface DiffingBroccoliPlugin {
30+
rebuild(diff: DiffResult): (Promise<any>| void);
31+
cleanup ? () : void;
32+
}
33+
34+
35+
type DiffingPluginWrapperFactory = (inputTrees: (BroccoliTree | BroccoliTree[]), options?) =>
36+
BroccoliTree;
37+
38+
39+
class DiffingPluginWrapper implements BroccoliTree {
40+
treeDiffer: TreeDiffer;
41+
initialized = false;
42+
wrappedPlugin: DiffingBroccoliPlugin = null;
43+
inputTree = null;
44+
inputTrees = null;
45+
description = null;
46+
47+
// props monkey-patched by broccoli builder:
48+
inputPath = null;
49+
cachePath = null;
50+
outputPath = null;
51+
52+
53+
constructor(private pluginClass, private wrappedPluginArguments) {
54+
if (Array.isArray(wrappedPluginArguments[0])) {
55+
this.inputTrees = wrappedPluginArguments[0];
56+
} else {
57+
this.inputTree = wrappedPluginArguments[0];
58+
}
59+
60+
this.description = this.pluginClass.name;
61+
}
62+
63+
64+
rebuild() {
65+
let firstRun = !this.initialized;
66+
this.init();
67+
68+
let diffResult = this.treeDiffer.diffTree();
69+
diffResult.log(!firstRun);
70+
71+
var rebuildPromise = this.wrappedPlugin.rebuild(diffResult);
72+
73+
if (rebuildPromise) {
74+
return (<Promise<any>>rebuildPromise).then(this.relinkOutputAndCachePaths.bind(this));
75+
}
76+
77+
this.relinkOutputAndCachePaths();
78+
}
79+
80+
81+
private relinkOutputAndCachePaths() {
82+
// just symlink the cache and output tree
83+
fs.rmdirSync(this.outputPath);
84+
symlinkOrCopy.sync(this.cachePath, this.outputPath);
85+
}
86+
87+
88+
private init() {
89+
if (!this.initialized) {
90+
this.initialized = true;
91+
this.treeDiffer = new TreeDiffer(this.inputPath);
92+
this.wrappedPlugin =
93+
new this.pluginClass(this.inputPath, this.cachePath, this.wrappedPluginArguments[1]);
94+
}
95+
}
96+
97+
98+
cleanup() {
99+
if (this.wrappedPlugin.cleanup) {
100+
this.wrappedPlugin.cleanup();
101+
}
102+
}
103+
}

tools/broccoli/traceur/index.ts

Lines changed: 16 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -1,66 +1,38 @@
1-
/// <reference path="../broccoli.d.ts" />
2-
/// <reference path="../broccoli-writer.d.ts" />
31
/// <reference path="../../typings/fs-extra/fs-extra.d.ts" />
42
/// <reference path="../../typings/node/node.d.ts" />
53

64
import fs = require('fs');
75
import fse = require('fs-extra');
86
import path = require('path');
9-
import TreeDiffer = require('../tree-differ');
10-
import Writer = require('broccoli-writer');
7+
import {wrapDiffingPlugin, DiffingBroccoliPlugin, DiffResult} from '../diffing-broccoli-plugin';
118

129
let traceur = require('../../../../tools/transpiler');
13-
let symlinkOrCopy = require('symlink-or-copy');
1410
let xtend = require('xtend');
1511

1612

17-
export = traceurCompiler;
18-
19-
function traceurCompiler(inputTree, destExtension, destSourceMapExtension, options) {
20-
return new TraceurCompiler(inputTree, destExtension, destSourceMapExtension, options);
21-
}
22-
23-
traceurCompiler['RUNTIME_PATH'] = traceur.RUNTIME_PATH;
24-
25-
26-
class TraceurCompiler implements BroccoliTree {
27-
treeDiffer: TreeDiffer;
28-
initialized = false;
29-
30-
// props monkey-patched by broccoli builder:
31-
inputPath = null;
32-
cachePath = null;
33-
outputPath = null;
34-
35-
36-
constructor(public inputTree: BroccoliTree, private destExtension: string,
37-
private destSourceMapExtension: string, private options: {[key: string]: any}) {}
13+
class DiffingTraceurCompiler implements DiffingBroccoliPlugin {
14+
constructor(public inputPath: string, public cachePath: string, public options) {}
3815

3916

40-
rebuild() {
41-
let firstRun = !this.initialized;
42-
this.init();
43-
44-
let diffResult = this.treeDiffer.diffTree();
45-
diffResult.log(!firstRun);
46-
47-
diffResult.changedPaths.forEach((changedFilePath) => {
17+
rebuild(treeDiff: DiffResult) {
18+
treeDiff.changedPaths.forEach((changedFilePath) => {
4819
var extension = path.extname(changedFilePath).toLowerCase();
4920
if (extension === '.js' || extension === '.es6' || extension === '.cjs') {
50-
var options = xtend({filename: changedFilePath}, this.options);
21+
var traceurOpts = xtend({filename: changedFilePath}, this.options.traceurOptions);
5122

5223
var fsOpts = {encoding: 'utf-8'};
5324
var absoluteInputFilePath = path.join(this.inputPath, changedFilePath);
5425
var sourcecode = fs.readFileSync(absoluteInputFilePath, fsOpts);
5526

56-
var result = traceur.compile(options, changedFilePath, sourcecode);
27+
var result = traceur.compile(traceurOpts, changedFilePath, sourcecode);
5728

5829
// TODO: we should fix the sourceMappingURL written by Traceur instead of overriding
5930
// (but we might switch to typescript first)
60-
var mapFilepath = changedFilePath.replace(/\.\w+$/, '') + this.destSourceMapExtension;
31+
var mapFilepath =
32+
changedFilePath.replace(/\.\w+$/, '') + this.options.destSourceMapExtension;
6133
result.js = result.js + '\n//# sourceMappingURL=./' + path.basename(mapFilepath);
6234

63-
var destFilepath = changedFilePath.replace(/\.\w+$/, this.destExtension);
35+
var destFilepath = changedFilePath.replace(/\.\w+$/, this.options.destExtension);
6436
var destFile = path.join(this.cachePath, destFilepath);
6537
fse.mkdirsSync(path.dirname(destFile));
6638
fs.writeFileSync(destFile, result.js, fsOpts);
@@ -71,25 +43,15 @@ class TraceurCompiler implements BroccoliTree {
7143
}
7244
});
7345

74-
diffResult.removedPaths.forEach((removedFilePath) => {
75-
var destFilepath = removedFilePath.replace(/\.\w+$/, this.destExtension);
46+
treeDiff.removedPaths.forEach((removedFilePath) => {
47+
var destFilepath = removedFilePath.replace(/\.\w+$/, this.options.destExtension);
7648
var absoluteOuputFilePath = path.join(this.cachePath, destFilepath);
7749
fs.unlinkSync(absoluteOuputFilePath);
7850
});
79-
80-
// just symlink the cache and output tree
81-
fs.rmdirSync(this.outputPath);
82-
symlinkOrCopy.sync(this.cachePath, this.outputPath);
83-
}
84-
85-
86-
private init() {
87-
if (!this.initialized) {
88-
this.initialized = true;
89-
this.treeDiffer = new TreeDiffer(this.inputPath);
90-
}
9151
}
52+
}
9253

54+
let transpileWithTraceur = wrapDiffingPlugin(DiffingTraceurCompiler);
55+
let TRACEUR_RUNTIME_PATH = traceur.RUNTIME_PATH;
9356

94-
cleanup() {}
95-
}
57+
export {transpileWithTraceur as default, TRACEUR_RUNTIME_PATH};

tools/broccoli/tree-differ.spec.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33

44
let mockfs = require('mock-fs');
55
import fs = require('fs');
6-
import TreeDiffer = require('./tree-differ');
6+
import {TreeDiffer} from './tree-differ';
77

88

99
describe('TreeDiffer', () => {

tools/broccoli/tree-differ.ts

Lines changed: 12 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,7 @@ import fs = require('fs');
44
import path = require('path');
55

66

7-
export = TreeDiffer;
8-
9-
class TreeDiffer {
7+
export class TreeDiffer {
108
private fingerprints: {[key: string]: string} = Object.create(null);
119
private nextFingerprints: {[key: string]: string} = Object.create(null);
1210
private rootDirName: string;
@@ -15,15 +13,15 @@ class TreeDiffer {
1513

1614

1715
public diffTree(): DiffResult {
18-
let result = new DiffResult(this.rootDirName);
16+
let result = new DirtyCheckingDiffResult(this.rootDirName);
1917
this.dirtyCheckPath(this.rootPath, result);
2018
this.detectDeletionsAndUpdateFingerprints(result);
2119
result.endTime = Date.now();
2220
return result;
2321
}
2422

2523

26-
private dirtyCheckPath(rootDir: string, result: DiffResult) {
24+
private dirtyCheckPath(rootDir: string, result: DirtyCheckingDiffResult) {
2725
fs.readdirSync(rootDir).forEach((segment) => {
2826
let absolutePath = path.join(rootDir, segment);
2927
let pathStat = fs.statSync(absolutePath);
@@ -76,7 +74,15 @@ class TreeDiffer {
7674
}
7775

7876

79-
class DiffResult {
77+
export interface DiffResult {
78+
changedPaths: string[];
79+
removedPaths: string[];
80+
log(verbose: boolean): void;
81+
toString(): string;
82+
}
83+
84+
85+
class DirtyCheckingDiffResult {
8086
public filesChecked: number = 0;
8187
public directoriesChecked: number = 0;
8288
public changedPaths: string[] = [];

0 commit comments

Comments
 (0)