Comparing version 2.1.4 to 2.2.0
138
lib/index.js
@@ -13,4 +13,4 @@ 'use strict'; | ||
var checkDir = require('checkdir'); | ||
var eventEmitter = new events.EventEmitter(); | ||
var nodefn = require('when/node'); | ||
var crypto = require('crypto'); | ||
var rimraf = nodefn.lift(require('rimraf')); | ||
@@ -20,7 +20,10 @@ var mkdirp = nodefn.lift(require('mkdirp')); | ||
// TODO: Consider using node-glob module instead of readdirp + through2 | ||
// I think that will simplify the code. | ||
var extname = function(filePath) { | ||
var ext = path.extname(filePath); | ||
if (babyTolk.targetExtensionMap[ext]) return ext; | ||
if (babyTolk.targetExtensionMap[ext]) { return ext; } | ||
return pathCompleteExtname(filePath); | ||
} | ||
}; | ||
@@ -51,3 +54,3 @@ var replaceExtension = function(filePath, newExtension) { | ||
).map(excl => excl.path) | ||
) | ||
); | ||
@@ -58,5 +61,16 @@ options.directoryFilter = options.directoryFilter.length ? options.directoryFilter : null; | ||
return options; | ||
} | ||
}; | ||
var createHash = function(data) { | ||
var shasum = crypto.createHash('sha1'); | ||
shasum.update(data); | ||
return shasum.digest('hex'); | ||
}; | ||
var noop = () => null | ||
module.exports = function(inputDir, outputDir, options) { | ||
var eventEmitter = new events.EventEmitter(); | ||
var shasFilename = '.shas.json'; | ||
var fileWhitelist = [shasFilename]; | ||
var filesCopied = 0; | ||
@@ -66,2 +80,5 @@ options = options || {}; | ||
var abortRequested = false; | ||
var shas = []; | ||
var shasPath = path.join(outputDir, shasFilename); | ||
var existingShas; | ||
@@ -72,9 +89,48 @@ // Default compile and minify options to `true` | ||
var babyTolkOptions = {}; | ||
babyTolkOptions.minify = options.minify; | ||
babyTolkOptions.sourceMap = options.sourcemaps; | ||
var babyTolkOptions = { | ||
minify: options.minify, | ||
sourceMap: options.sourcemaps, | ||
inputSha: true, | ||
}; | ||
if (options.exclusions) { useExclusionsApi(options); } | ||
var compileAndCopy = function() { | ||
var readAndValidateShas = function() { | ||
var getMainFile = sha => { | ||
var files = sha.output; | ||
var mainFile = files[files.length - 1]; | ||
return path.join(outputDir, mainFile); | ||
}; | ||
var getInputFile = sha => path.join(inputDir, sha.input); | ||
return fsp.readFile(shasPath, 'utf8').then(contents => { | ||
existingShas = JSON.parse(contents); | ||
var matchingCompilerShas = existingShas.filter(sha => | ||
babyTolk.getTransformId(getInputFile(sha), babyTolkOptions) === sha.type | ||
); | ||
var files = existingShas.map(sha => | ||
sha && fsp.readFile(getMainFile(sha), 'utf8').then(contents => contents, noop) | ||
); | ||
return when.all(files).then(filesContents => | ||
matchingCompilerShas.filter( | ||
(sha, i) => filesContents[i] && (sha.outputSha === createHash(filesContents[i])) | ||
) | ||
); | ||
}) | ||
.then(filtered => { | ||
if (!Array.isArray(filtered)) { return; } | ||
return { | ||
input: filtered.map(x => x.input), | ||
// Flatten output files to single array | ||
output: [].concat.apply([], filtered.map(x => x.output)) | ||
}; | ||
}, noop // ignore errors (shas are not required, just good for perf) | ||
); | ||
}; | ||
var compileAndCopy = function(reusableFiles) { | ||
return when.promise(function(resolve, reject) { | ||
@@ -93,2 +149,4 @@ options.root = inputDir; | ||
var addFileToWhitelist = filePath => { fileWhitelist.push(filePath); }; | ||
var fileDone = function() { | ||
@@ -108,5 +166,19 @@ if (previousDir !== file.parentDir) { | ||
var compile = function() { | ||
if (reusableFiles && Array.isArray(reusableFiles.input)) { | ||
var reuseExistingFile = reusableFiles.input.indexOf(file.path) > -1; | ||
if (reuseExistingFile) { | ||
var previousSha = existingShas.find(sha => sha.input === file.path); | ||
if (previousSha) { shas.push(previousSha); } | ||
eventEmitter.emit('compile-reuse', file); | ||
previousSha.output.forEach(addFileToWhitelist); | ||
return when.resolve(false); | ||
} | ||
} | ||
eventEmitter.emit('compile-start', file); | ||
return babyTolk.read(file.fullPath, babyTolkOptions).then(function(compiled) { | ||
var writeFiles = []; | ||
var relativePath = path.relative(inputDir, compiled.inputPath); | ||
var writeFiles = [], fileNames = []; | ||
@@ -127,3 +199,5 @@ var fileName = file.name; | ||
}); | ||
writeFiles.push(fsp.writeFile(compiledOutputFullPath + '.map', JSON.stringify(compiled.sourcemap))); | ||
var srcMapFileName = compiledOutputFullPath + '.map'; | ||
writeFiles.push(fsp.writeFile(srcMapFileName, JSON.stringify(compiled.sourcemap))); | ||
fileNames.push(path.relative(outputDir, srcMapFileName)); | ||
if (compiled.extension === '.css') { | ||
@@ -139,6 +213,17 @@ /*# sourceMappingURL=screen.css.map */ | ||
writeFiles.push(fsp.writeFile(compiledOutputFullPath, compiled.result)); | ||
fileNames.push(path.relative(outputDir, compiledOutputFullPath)); | ||
shas.push({ | ||
type: compiled.transformId, | ||
inputSha: compiled.inputSha, | ||
outputSha: createHash(compiled.result), | ||
input: relativePath, | ||
output: fileNames | ||
}); | ||
eventEmitter.emit('compile-done', file); | ||
return when.all(writeFiles); | ||
return when.all(writeFiles).then(() => { | ||
fileNames.forEach(addFileToWhitelist); | ||
}); | ||
}); | ||
}; | ||
}; // End compile Fn | ||
@@ -149,2 +234,3 @@ var copy = function(renameToSrc) { | ||
var ws = fs.createWriteStream(writePath); | ||
addFileToWhitelist(path.relative(outputDir, writePath)); | ||
rs.pipe(ws).on('error', reject); | ||
@@ -185,7 +271,23 @@ ws.on('finish', fileDone).on('error', reject); | ||
})).on('error', reject); | ||
}).then(filesCopied => { | ||
eventEmitter.emit('cleaning-up'); | ||
return when.promise((resolve, reject) => { | ||
readdirp({ | ||
root: outputDir, | ||
fileFilter: file => fileWhitelist.indexOf(file.path) === -1 | ||
}).pipe(through.obj((file, _, next) => { | ||
fs.unlink(file.fullPath); | ||
next(); | ||
}, (finish) => {resolve(filesCopied); finish();})).on('error', reject); | ||
}); | ||
}); | ||
}; | ||
var promise = rimraf(outputDir) | ||
.then(compileAndCopy) | ||
var promise = | ||
readAndValidateShas() | ||
.then(compileAndCopy) | ||
.then(numFiles => | ||
fsp.writeFile(shasPath, JSON.stringify(shas, null, 2)).then(() => numFiles) | ||
) | ||
// TODO: Don't delete dir after abort. Leave as is instead. | ||
.catch(function(err) { | ||
@@ -218,4 +320,4 @@ // If there was an error then back out, delete the output dir and forward | ||
{ bowerComponents: bowerComponents.exists } | ||
) | ||
}) | ||
} | ||
); | ||
}); | ||
}; |
{ | ||
"name": "baconize", | ||
"version": "2.1.4", | ||
"version": "2.2.0", | ||
"description": "Compile static site for production (with sourcemaps), auto-compiles files like `app.coffee -> app.js`", | ||
"main": "lib/index.js", | ||
"scripts": { | ||
"test": "mocha", | ||
"test": "mocha --timeout 5000", | ||
"example": "node examples/compile-site.js" | ||
@@ -31,3 +31,3 @@ }, | ||
"dependencies": { | ||
"baby-tolk": "^4.1.1", | ||
"baby-tolk": "^4.5.0", | ||
"checkdir": "^1.0.0", | ||
@@ -34,0 +34,0 @@ "micromatch": "^2.3.11", |
@@ -346,3 +346,3 @@ 'use strict'; | ||
before(clearDir); | ||
after(clearDir); | ||
// after(clearDir); | ||
@@ -487,2 +487,172 @@ it('should compile compilable files and copy all others', function () { | ||
describe('compile minified with sourcemaps (SHA test)', function() { | ||
// | ||
// before(clearDir); | ||
after(clearDir); | ||
before(() => { | ||
// Mess up the dir a bit to make sure we recompile files as needed. | ||
var styl = fs.readFile(getPathOut('styles/main.css'), 'utf8').then(content => | ||
fs.writeFile(getPathOut('styles/main.css'), content + ' ') | ||
); | ||
var html = fs.unlink(getPathOut('index.html')); | ||
var randomFile = fs.writeFile(getPathOut('bla.html'), 'SHOULD BE REMOVED'); | ||
return when.all([styl, html, randomFile]); | ||
}); | ||
it('should compile compilable files and copy all others', function () { | ||
var options = { | ||
blacklist: ['dont-compile/**'], | ||
directoryFilter: ['!dont-copy'], | ||
compile: true, | ||
minify: true, | ||
sourcemaps: true | ||
}; | ||
var bacon = baconize(getPathIn(), getPathOut(), options); | ||
var dirs = []; | ||
bacon.events.on('chdir', function(dir) { dirs.push(dir); }); | ||
var compiledFiles = []; | ||
var compilationReuseFiles = []; | ||
var lastStartedFile; | ||
bacon.events.on('compile-start', function(file) { | ||
lastStartedFile = file.path; | ||
}); | ||
bacon.events.on('compile-done', function(file) { | ||
if (lastStartedFile === file.path) { | ||
compiledFiles.push(file.path); | ||
} else { | ||
expect.fail('Unexpected compile-done event'); | ||
} | ||
}); | ||
bacon.events.on('compile-reuse', function(file) { | ||
compilationReuseFiles.push(file.path); | ||
}); | ||
return bacon.then(function(num) { | ||
return expect.promise.all([ | ||
expect(num, 'to be', 9), | ||
expect(dirs, 'to contain', '', 'dont-compile', 'scripts', 'styles').and('to have length', 4), | ||
expect(compiledFiles, 'to contain', 'index.jade', 'styles/main.styl').and('to have length', 2), | ||
expect(compilationReuseFiles, 'to contain', | ||
'about.html','scripts/log.babel.js', 'styles/typography.css', | ||
'scripts/iterate.js', 'scripts/main.coffee').and('to have length', 5), | ||
]); | ||
}); | ||
}); | ||
describe('after compilation', function() { | ||
it('should have all compiled files', function() { | ||
return when.all([ | ||
fs.readFile(getPathOut('index.html')), | ||
fs.readFile(getPathOut('styles/main.css')), | ||
fs.readFile(getPathOut('scripts/main.js')), | ||
fs.readFile(getPathOut('scripts/iterate.js')), | ||
fs.readFile(getPathOut('about.html')), | ||
fs.readFile(getPathOut('dont-compile/foo.coffee')), | ||
fs.readFile(getPathOut('styles/typography.css')) | ||
]).then(function(results) { | ||
return expect.promise.all([ | ||
expect(results[0].toString(), 'to contain', 'saying \'hello\'.</p>'), | ||
expect(results[1].toString(), 'to contain', 'body{'), | ||
expect(results[2].toString(), 'to contain', 'console.log("H'), | ||
expect(results[3].toString(), 'to contain', 'for(var stuff=[1,2,3,4,5]'), | ||
expect(results[4].toString(), 'to contain', '<html><head>'), | ||
expect(results[5].toString(), 'to contain', 'console.log "T'), | ||
expect(results[6].toString(), 'to contain', 'font-family:arial}') | ||
]); | ||
}); | ||
}); | ||
it('should URL link to source maps', function() { | ||
return when.all([ | ||
fs.readFile(getPathOut('styles/main.css')), | ||
fs.readFile(getPathOut('scripts/main.js')), | ||
fs.readFile(getPathOut('scripts/iterate.js')), | ||
fs.readFile(getPathOut('styles/typography.css')) | ||
]).then(function(results) { | ||
return expect.promise.all([ | ||
expect(results[0].toString(), 'to contain', '/*# sourceMappingURL=main.css.map*/'), | ||
expect(results[1].toString(), 'to contain', '//# sourceMappingURL=main.js.map'), | ||
expect(results[2].toString(), 'to contain', '//# sourceMappingURL=iterate.js.map'), | ||
expect(results[3].toString(), 'to contain', '/*# sourceMappingURL=typography.css.map*/') | ||
]); | ||
}); | ||
}); | ||
it('should have source maps', function() { | ||
return when.all([ | ||
fs.readFile(getPathOut('styles/main.css.map')), | ||
fs.readFile(getPathOut('scripts/main.js.map')), | ||
fs.readFile(getPathOut('scripts/iterate.js.map')), | ||
fs.readFile(getPathOut('styles/typography.css.map')) | ||
]).then(function(results) { | ||
return expect.promise.all([ | ||
expect(results[0].toString(), 'to contain', '"file":"main.css"') | ||
.and('to contain', '"sources":["main.styl"]'), | ||
expect(results[1].toString(), 'to contain', '"file":"main.js"') | ||
.and('to contain', '"sources":["main.coffee"]'), | ||
expect(results[2].toString(), 'to contain', '"file":"iterate.js"') | ||
.and('to contain', '"sources":["iterate.src.js"]'), | ||
expect(results[3].toString(), 'to contain', '"file":"typography.css"') | ||
.and('to contain', '"sources":["typography.src.css"]'), | ||
]); | ||
}); | ||
}); | ||
it('should copy pre-compiled source files', function() { | ||
return when.all([ | ||
fs.readFile(getPathOut('index.jade')), | ||
fs.readFile(getPathOut('about.src.html')), | ||
fs.readFile(getPathOut('scripts/iterate.src.js')), | ||
fs.readFile(getPathOut('styles/typography.src.css')) | ||
]).then(function(results) { | ||
return expect.promise.all([ | ||
expect(results[0].toString(), 'to contain', 'html(lang="en")'), | ||
expect(results[1].toString(), 'to contain', ' <meta charset="utf-8">'), | ||
expect(results[2].toString(), 'to contain', 'for (var i = 0; i < stuff.length; i++) {'), | ||
expect(results[3].toString(), 'to contain', ' font-family: arial;') | ||
]); | ||
}); | ||
}); | ||
it('should not compile blacklist matches', function() { | ||
return fs.readFile(getPathOut('dont-compile/foo.js')) | ||
.then(function() { | ||
return expect.fail('`dont-compile/foo.js` should not be copied'); | ||
}, function(err) { | ||
return expect(err.code, 'to be', 'ENOENT'); | ||
}); | ||
}); | ||
it('should not copy ignore paths', function() { | ||
return fs.stat(getPathOut('dont-copy')) | ||
.then(function() { | ||
return expect.fail('`dont-copy` directory should not be copied'); | ||
}, function(err) { | ||
return expect(err.code, 'to be', 'ENOENT'); | ||
}); | ||
}); | ||
it('should remove files that weren\'t in src dir', function() { | ||
return fs.readFile(getPathOut('bla.html')) | ||
.then(function() { | ||
return expect.fail( | ||
'`bla.html` should *not* be copied because it\'s not in src directory' | ||
); | ||
}, function(err) { | ||
return expect(err.code, 'to be', 'ENOENT'); | ||
}); | ||
}); | ||
}); | ||
}); | ||
describe('aborted compile', function() { | ||
@@ -489,0 +659,0 @@ |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
70898
1393
Updatedbaby-tolk@^4.5.0