Skip to content
This repository has been archived by the owner on Oct 9, 2020. It is now read-only.

Commit

Permalink
buildTree/buildSFX/compileOutputs take optional compiler cache
Browse files Browse the repository at this point in the history
  • Loading branch information
insidewhy committed Jun 21, 2015
1 parent 4eaf386 commit 458dc8f
Show file tree
Hide file tree
Showing 4 changed files with 99 additions and 7 deletions.
21 changes: 15 additions & 6 deletions lib/builder.js
Original file line number Diff line number Diff line change
Expand Up @@ -214,7 +214,10 @@ Builder.prototype.config = function(config) {
pluginLoader.config(cfg);
};

Builder.prototype.build = function(expression, outFile, opts) {
/**
* @param {Object} [cache] - Optional cache for storing/reusing compiled modules from other calls to build/buildTree/buildSFX. The cache is populated with an entry for each `load` and is keyed on `load.name`.
*/
Builder.prototype.build = function(expression, outFile, opts, cache) {
var self = this;

// Allow passing opts as second argument.
Expand All @@ -230,7 +233,7 @@ Builder.prototype.build = function(expression, outFile, opts) {

return this.trace(expression)
.then(function(tree) {
return self.buildTree(tree, outFile, opts);
return self.buildTree(tree, outFile, opts, cache);
});
};

Expand All @@ -240,7 +243,10 @@ function addExtraOutputs(output, tree, opts) {
});
}

Builder.prototype.buildTree = function(tree, outFile, opts) {
/**
* @param {Object} [cache] - Optional cache for storing/reusing compiled modules, see also `build` method.
*/
Builder.prototype.buildTree = function(tree, outFile, opts, cache) {
var loader = this.loader;
var self = this;

Expand All @@ -252,7 +258,7 @@ Builder.prototype.buildTree = function(tree, outFile, opts) {

opts = processOpts(opts, outFile);

return compileOutputs(loader, tree, opts, false)
return compileOutputs(loader, tree, opts, false, cache)
.then(function(outputs) {
return writeOutputs(opts, outputs, loader.baseURL);
})
Expand All @@ -262,7 +268,10 @@ Builder.prototype.buildTree = function(tree, outFile, opts) {
});
};

Builder.prototype.buildSFX = function(expression, outFile, opts) {
/**
* @param {Object} [cache] - Optional cache for storing/reusing compiled modules, see also `build` method.
*/
Builder.prototype.buildSFX = function(expression, outFile, opts, cache) {
var loader = this.loader;
var self = this;

Expand All @@ -288,7 +297,7 @@ Builder.prototype.buildSFX = function(expression, outFile, opts) {
return traceExpression(this, expression, true)
.then(function(trace) {
tree = trace.tree;
return compileOutputs(loader, tree, opts, trace.entryPoints);
return compileOutputs(loader, tree, opts, trace.entryPoints, cache);
})
.then(function(outputs) {
return writeOutputs(opts, outputs, loader.baseURL);
Expand Down
35 changes: 34 additions & 1 deletion lib/compile.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ var asp = require('rsvp').denodeify;
var fs = require('fs');
var path = require('path');
var url = require('url');
var createHash = require('crypto').createHash;

var compilerMap = {
'amd': '../compilers/amd',
Expand All @@ -12,6 +13,31 @@ var compilerMap = {
'register': '../compilers/register'
};

function hashString(source) {
return createHash('md5').update(source).digest('hex');
}

function retrieveCachedOutput(cache, load) {
if (! cache)
return;

var entry = cache[load.name];
if (! entry)
return;

return hashString(load.source) === entry.sourceHash && entry.output;
}

function updateCacheEntry(cache, load, output) {
if (! cache)
return;

cache[load.name] = {
sourceHash: hashString(load.source),
output: output,
};
}

exports.compileLoad = compileLoad;
function compileLoad(loader, load, opts) {
if (load.metadata.build === false)
Expand All @@ -29,7 +55,7 @@ function compileLoad(loader, load, opts) {

// compiledTree is load tree with "source" and "output" set
exports.compileOutputs = compileOutputs;
function compileOutputs(loader, tree, opts, sfxEntryPoints) {
function compileOutputs(loader, tree, opts, sfxEntryPoints, cache) {
var modules = Object.keys(tree);

// store plugins with a bundle hook to allow post-processing
Expand Down Expand Up @@ -75,8 +101,15 @@ function compileOutputs(loader, tree, opts, sfxEntryPoints) {
if (load.metadata.loaderModule && load.metadata.loaderModule.bundle)
return;

var output = retrieveCachedOutput(cache, load);
if (output) {
outputs.push(output);
return;
}

return Promise.resolve(compileLoad(loader, load, opts))
.then(function(output) {
updateCacheEntry(cache, load, output);
outputs.push(output);
});
}));
Expand Down
3 changes: 3 additions & 0 deletions test/fixtures/test-cache-tree/simple.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
define([], function(module) {
console.log('I hate caches');
});
47 changes: 47 additions & 0 deletions test/test-build-cache.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
var Builder = require('../index');
var expect = require('chai').expect;

suite('Test compiler cache', function() {
var builder = new Builder('test/fixtures/test-cache-tree');
builder.config({ transpiler: 'babel' });

test('Use cache entry when available', function() {
var loadName = 'simple.js';
var outputPath = 'test/output/cached.js';
var cache = {};
var tree;

return builder.trace(loadName).then(function(_tree) {
tree = _tree;
return builder.buildTree(tree, null, {}, cache);
})
.then(function(output) {
var cacheEntry = cache[loadName];
expect(cacheEntry).to.be.an('object');
expect(cacheEntry.sourceHash).to.be.a('string');
var cacheOutput = cacheEntry.output;
expect(cacheOutput).to.be.an('object');

// poison cache
cacheOutput.source = cacheOutput.source.replace('hate', 'love');

return builder.buildTree(tree, null, {}, cache);
})
.then(function(output) {
// verify buildTree use poisoned cache rather than recompiling
var outputSource = output.source;
expect(outputSource).not.to.contain('hate caches');
expect(outputSource).to.contain('love caches');

// invalidate poisoned cache entry and rebuild
cache[loadName].sourceHash = 'out of date';
return builder.buildTree(tree, null, {}, cache);
})
.then(function(output) {
// verify original source is used once more
var outputSource = output.source;
expect(outputSource).to.contain('hate caches');
expect(outputSource).not.to.contain('love caches');
});
});
});

0 comments on commit 458dc8f

Please sign in to comment.