broccoli-funnel
Advanced tools
Comparing version 0.2.10 to 0.2.11
147
index.js
'use strict'; | ||
var fs = require('fs'); | ||
var path = require('path'); | ||
var path = require('path-posix'); | ||
var mkdirp = require('mkdirp'); | ||
@@ -12,2 +12,4 @@ var walkSync = require('walk-sync'); | ||
var debug = require('debug'); | ||
var FSTree = require('fs-tree-diff'); | ||
var rimraf = require('rimraf'); | ||
@@ -44,4 +46,7 @@ function makeDictionary() { | ||
this._persistentOutput = true; | ||
this._includeFileCache = makeDictionary(); | ||
this._destinationPathCache = makeDictionary(); | ||
this._currentTree = new FSTree(); | ||
@@ -55,2 +60,3 @@ var keys = Object.keys(options || {}); | ||
this.destDir = this.destDir || '/'; | ||
this.count = 0; | ||
@@ -158,3 +164,3 @@ if (this.files && typeof this.files === 'function') { | ||
if (fs.existsSync(inputPath)) { | ||
fs.rmdirSync(this.outputPath); | ||
rimraf.sync(this.outputPath); | ||
this._copy(inputPath, this.destPath); | ||
@@ -176,35 +182,80 @@ } else if (this.allowEmpty) { | ||
function ensureRelative(string) { | ||
if (string.charAt(0) === '/') { | ||
return string.substring(1); | ||
} | ||
return string; | ||
} | ||
Funnel.prototype._processEntries = function(entries) { | ||
return entries.filter(function(entry) { | ||
// support the second set of filters walk-sync does not support | ||
// * regexp | ||
// * excludes | ||
return this.includeFile(entry.relativePath); | ||
}, this).map(function(entry) { | ||
var relativePath = entry.relativePath; | ||
// the destination paths "absolute" is actually stlil relative to the FSTree | ||
entry.relativePath = ensureRelative(this.lookupDestinationPath(ensureRelative(path.join(this.destDir, relativePath)))); | ||
this.outputToInputMappings[entry.relativePath] = relativePath; | ||
return entry; | ||
}, this); | ||
}; | ||
Funnel.prototype._processPaths = function(paths, outputToInputMappings) { | ||
return paths. | ||
slice(0). | ||
filter(this.includeFile, this). | ||
map(function(relativePath) { | ||
var output = ensureRelative(this.lookupDestinationPath(ensureRelative(path.join(this.destDir, relativePath)))); | ||
this.outputToInputMappings[output] = relativePath; | ||
return output; | ||
}, this); | ||
}; | ||
Funnel.prototype.processFilters = function(inputPath) { | ||
var files; | ||
var nextTree; | ||
this.outputToInputMappings = {}; // we allow users to rename files | ||
if (this.files && !this.exclude && !this.include) { | ||
files = this.files.slice(0); //clone to be compatible with walkSync | ||
// clone to be compatible with walkSync | ||
nextTree = FSTree.fromPaths(this._processPaths(this.files)); | ||
} else { | ||
var entries; | ||
if (this._matchedWalk) { | ||
files = walkSync(inputPath, this.include); | ||
entries = walkSync.entries(inputPath, undefined, this.include); | ||
} else { | ||
files = walkSync(inputPath); | ||
entries = walkSync.entries(inputPath); | ||
} | ||
nextTree = new FSTree({ | ||
entries: this._processEntries(entries) | ||
}); | ||
} | ||
var relativePath, destRelativePath, fullInputPath, fullOutputPath; | ||
var patch = this._currentTree.calculatePatch(nextTree); | ||
var count = 0; | ||
for (var i = 0, l = files.length; i < l; i++) { | ||
relativePath = files[i]; | ||
this._currentTree = nextTree; | ||
if (this.includeFile(relativePath)) { | ||
count++; | ||
fullInputPath = path.join(inputPath, relativePath); | ||
destRelativePath = this.lookupDestinationPath(relativePath); | ||
fullOutputPath = path.join(this.destPath, destRelativePath); | ||
this._debug('patch: %o', patch); | ||
this.processFile(fullInputPath, fullOutputPath, relativePath); | ||
} | ||
} | ||
var outputPath = this.outputPath; | ||
patch.forEach(function(entry) { | ||
this._applyPatch(entry, inputPath, outputPath); | ||
}, this); | ||
var count = nextTree.size; | ||
this._debug('processFilters %o', { | ||
in: new Date() - this._buildStart + 'ms', | ||
filesFound: files.length, | ||
filesFound: this._currentTree.size, | ||
filesProcessed: count, | ||
operations: patch.length, | ||
inputPath: inputPath, | ||
@@ -215,2 +266,41 @@ destPath: this.destPath | ||
Funnel.prototype._applyPatch = function applyPatch(entry, inputPath, _outputPath) { | ||
var outputToInput = this.outputToInputMappings; | ||
var operation = entry[0]; | ||
var outputRelative = entry[1]; | ||
if (!outputRelative) { | ||
// broccoli itself maintains the roots, we can skip any operation on them | ||
return; | ||
} | ||
var outputPath = _outputPath + '/' + outputRelative; | ||
this._debug('%s %s', operation, outputPath); | ||
if (operation === 'change') { | ||
operation = 'create'; | ||
} | ||
switch (operation) { | ||
case 'unlink' : | ||
fs.unlinkSync(outputPath); | ||
break; | ||
case 'rmdir' : | ||
fs.rmdirSync(outputPath); | ||
break; | ||
case 'mkdir' : | ||
fs.mkdirSync(outputPath); | ||
break; | ||
case 'create'/* also change */ : | ||
var relativePath = outputToInput[outputRelative]; | ||
if (relativePath === undefined) { | ||
relativePath = outputToInput['/' + outputRelative]; | ||
} | ||
this.processFile(inputPath + '/' + relativePath, outputPath, relativePath); | ||
break; | ||
default: throw new Error('Unknown operation: ' + operation); | ||
} | ||
}; | ||
Funnel.prototype.lookupDestinationPath = function(relativePath) { | ||
@@ -296,10 +386,19 @@ if (this._destinationPathCache[relativePath] !== undefined) { | ||
Funnel.prototype._copy = function(sourcePath, destPath) { | ||
var destDir = path.dirname(destPath); | ||
if (!fs.existsSync(destDir)) { | ||
mkdirp.sync(destDir); | ||
var destDir = path.dirname(destPath); | ||
try { | ||
symlinkOrCopy.sync(sourcePath, destPath); | ||
} catch(e) { | ||
if (!fs.existsSync(destDir)) { | ||
mkdirp.sync(destDir); | ||
} | ||
try { | ||
fs.unlinkSync(destPath); | ||
} catch(e) { | ||
} | ||
symlinkOrCopy.sync(sourcePath, destPath); | ||
} | ||
symlinkOrCopy.sync(sourcePath, destPath); | ||
}; | ||
module.exports = Funnel; |
{ | ||
"name": "broccoli-funnel", | ||
"version": "0.2.10", | ||
"version": "0.2.11", | ||
"description": "Broccoli plugin that allows you to filter files selected from an input node down based on regular expressions.", | ||
@@ -24,4 +24,8 @@ "main": "index.js", | ||
"debug": "^2.2.0", | ||
"fast-ordered-set": "^1.0.0", | ||
"fs-tree-diff": "0.3.0", | ||
"minimatch": "^2.0.1", | ||
"mkdirp": "^0.5.0", | ||
"path-posix": "^1.0.0", | ||
"rimraf": "^2.4.3", | ||
"symlink-or-copy": "^1.0.0", | ||
@@ -32,8 +36,9 @@ "walk-sync": "^0.2.5" | ||
"broccoli": "^0.15.0", | ||
"rimraf": "^2.3.2", | ||
"rsvp": "^3.0.14", | ||
"chai": "^3.2.0", | ||
"expect.js": "^0.3.1", | ||
"mocha": "~1.18.2", | ||
"mocha-jshint": "0.0.9" | ||
"mocha-jshint": "0.0.9", | ||
"rimraf": "^2.3.2", | ||
"rsvp": "^3.0.14" | ||
} | ||
} |
@@ -16,3 +16,3 @@ 'use strict'; | ||
describe('broccoli-funnel', function(){ | ||
var fixturePath = path.join(__dirname, 'fixtures'); | ||
var fixturePath = __dirname + '/fixtures'; | ||
var builder; | ||
@@ -26,5 +26,32 @@ | ||
describe('rebuilding', function() { | ||
it('correctly rebuilds', function() { | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
include: ['**/*.js'] | ||
}); | ||
builder = new broccoli.Builder(node); | ||
return builder.build() | ||
.then(function(results) { | ||
var outputPath = results.directory; | ||
expect(walkSync(outputPath, ['**/*.js'])).to.eql(walkSync(inputPath, ['**/*.js'])); | ||
var mutatedFile = inputPath + '/' + 'subdir1/subsubdir2/some.js'; | ||
fs.writeFileSync(mutatedFile, fs.readFileSync(mutatedFile)); | ||
return builder.build(); | ||
}) | ||
.then(function(results) { | ||
var outputPath = results.directory; | ||
expect(walkSync(outputPath, ['**/*.js'])).to.eql(walkSync(inputPath, ['**/*.js'])); | ||
}); | ||
}); | ||
}); | ||
describe('processFile', function() { | ||
it('is not called when simply linking roots (aka no include/exclude)', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -48,3 +75,3 @@ processFile: function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -57,3 +84,8 @@ include: [ /.png$/, /.js$/ ], | ||
var relDestPath = destPath.replace(this.outputPath, '__output_path__'); | ||
processFileArguments.push([relSourcePath, relDestPath, relativePath]); | ||
processFileArguments.push([ | ||
relSourcePath, | ||
relDestPath, | ||
relativePath | ||
]); | ||
} | ||
@@ -65,9 +97,12 @@ }); | ||
.then(function(results) { | ||
var expected = [ | ||
[ path.join('__input_path__', 'subdir1/subsubdir1/foo.png'), | ||
path.join('__output_path__', 'foo/subdir1/subsubdir1/foo.png'), | ||
'subdir1/subsubdir1/foo.png' ], | ||
[ path.join('__input_path__', 'subdir1/subsubdir2/some.js'), | ||
path.join('__output_path__', 'foo/subdir1/subsubdir2/some.js'), | ||
'subdir1/subsubdir2/some.js' ] | ||
[ '__input_path__/subdir1/subsubdir1/foo.png', | ||
'__output_path__/foo/subdir1/subsubdir1/foo.png', | ||
'subdir1/subsubdir1/foo.png' | ||
], | ||
[ '__input_path__/subdir1/subsubdir2/some.js', | ||
'__output_path__/foo/subdir1/subsubdir2/some.js', | ||
'subdir1/subsubdir2/some.js' | ||
] | ||
]; | ||
@@ -80,3 +115,4 @@ | ||
it('is responsible for generating files in the destDir', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -96,3 +132,9 @@ include: [ /.png$/, /.js$/ ], | ||
expect(walkSync(outputPath)).to.eql([]); | ||
expect(walkSync(outputPath)).to.eql([ | ||
// only folders exist | ||
'foo/', | ||
'foo/subdir1/', | ||
'foo/subdir1/subsubdir1/', | ||
'foo/subdir1/subsubdir2/' | ||
]); | ||
}); | ||
@@ -102,3 +144,3 @@ }); | ||
it('works with mixed glob and RegExp includes', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -118,3 +160,9 @@ include: [ '**/*.png', /.js$/ ], | ||
expect(walkSync(outputPath)).to.eql([]); | ||
expect(walkSync(outputPath)).to.eql([ | ||
// only dir exist | ||
'foo/', | ||
'foo/subdir1/', | ||
'foo/subdir1/subsubdir1/', | ||
'foo/subdir1/subsubdir2/' | ||
]); | ||
}); | ||
@@ -125,3 +173,3 @@ }); | ||
it('correctly chooses _matchedWalk scenario', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node; | ||
@@ -140,3 +188,3 @@ node = new Funnel(inputPath, { include: [ '**/*.png', /.js$/ ] }); | ||
it('linking roots without srcDir/destDir, can rebuild without error', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath); | ||
@@ -161,3 +209,3 @@ | ||
it('simply returns a copy of the input node', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath); | ||
@@ -175,3 +223,3 @@ | ||
it('simply returns a copy of the input node at a nested destination', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -184,3 +232,3 @@ destDir: 'some-random' | ||
.then(function(results) { | ||
var outputPath = path.join(results.directory, 'some-random'); | ||
var outputPath = results.directory + '/some-random'; | ||
@@ -193,3 +241,3 @@ expect(walkSync(outputPath)).to.eql(walkSync(inputPath)); | ||
.then(function(results) { | ||
var outputPath = path.join(results.directory, 'some-random'); | ||
var outputPath = results.directory + '/some-random'; | ||
@@ -201,3 +249,3 @@ expect(walkSync(outputPath)).to.eql(walkSync(inputPath)); | ||
it('can properly handle the output path being a broken symlink', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -219,3 +267,3 @@ srcDir: 'subdir1' | ||
.then(function(results) { | ||
var restrictedInputPath = path.join(inputPath, 'subdir1'); | ||
var restrictedInputPath = inputPath + '/subdir1'; | ||
var outputPath = results.directory; | ||
@@ -228,3 +276,3 @@ | ||
it('simply returns a copy of the input node at a nested source', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -237,3 +285,3 @@ srcDir: 'subdir1' | ||
.then(function(results) { | ||
var restrictedInputPath = path.join(inputPath, 'subdir1'); | ||
var restrictedInputPath = inputPath + '/subdir1'; | ||
var outputPath = results.directory; | ||
@@ -247,3 +295,3 @@ | ||
.then(function(results) { | ||
var restrictedInputPath = path.join(inputPath, 'subdir1'); | ||
var restrictedInputPath = inputPath + '/subdir1'; | ||
var outputPath = results.directory; | ||
@@ -256,3 +304,3 @@ | ||
it('matches *.css', function() { | ||
var inputPath = path.join(fixturePath, 'dir1/subdir2'); | ||
var inputPath = fixturePath + '/dir1/subdir2'; | ||
var node = new Funnel(inputPath, { | ||
@@ -266,3 +314,6 @@ include: ['*.css'] | ||
var outputPath = results.directory; | ||
expect(walkSync(outputPath)).to.eql(['bar.css']); | ||
expect(walkSync(outputPath)).to.eql([ | ||
'bar.css' | ||
]); | ||
}); | ||
@@ -272,3 +323,3 @@ }); | ||
it('matches the deprecated: files *.css', function() { | ||
var inputPath = path.join(fixturePath, 'dir1/subdir2'); | ||
var inputPath = fixturePath + '/dir1/subdir2'; | ||
var oldWarn = console.warn; | ||
@@ -303,3 +354,3 @@ var message; | ||
it('does not error with input node at a missing nested source', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -332,3 +383,3 @@ srcDir: 'subdir3', | ||
function testFiltering(includes, excludes, files, expected) { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -363,3 +414,3 @@ include: includes, | ||
it('can take a list of files', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -392,3 +443,3 @@ files: [ | ||
it('so error if `files` and `include` are set', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
@@ -404,3 +455,3 @@ expect(function() { | ||
it('so error if `files` and `exclude` are set', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
@@ -418,5 +469,5 @@ expect(function() { | ||
it('can take files as a function', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var filesCounter = 0; | ||
var inputPath = fixturePath + '/dir1'; | ||
var filesByCounter = [ | ||
// rebuild 1: | ||
[ | ||
@@ -426,4 +477,10 @@ 'subdir1/subsubdir1/foo.png', | ||
], | ||
// rebuild 2: | ||
[ 'subdir1/subsubdir1/foo.png' ], | ||
// rebuild 3: | ||
[], | ||
// rebuild 4: | ||
['subdir1/subsubdir2/some.js'] | ||
@@ -434,3 +491,3 @@ ]; | ||
files: function() { | ||
return filesByCounter[filesCounter++]; | ||
return filesByCounter.shift(); | ||
} | ||
@@ -496,3 +553,3 @@ }); | ||
it('can take files as a function with exclude (includeCache needs to be cleared)', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var filesCounter = 0; | ||
@@ -631,3 +688,3 @@ var filesByCounter = [ | ||
it('combined filtering', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -652,3 +709,3 @@ exclude: [ /.png$/, /.js$/ ], | ||
it('creates its output directory even if no files are matched', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath, { | ||
@@ -670,7 +727,7 @@ exclude: [ /.*/ ] | ||
it('uses custom getDestinationPath function if provided', function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
var node = new Funnel(inputPath); | ||
node.getDestinationPath = function(relativePath) { | ||
return path.join('foo', relativePath); | ||
return 'foo/' + relativePath; | ||
}; | ||
@@ -683,3 +740,3 @@ | ||
expect(walkSync(path.join(outputPath, 'foo'))).to.eql(walkSync(inputPath)); | ||
expect(walkSync(outputPath + '/foo')).to.eql(walkSync(inputPath)); | ||
}); | ||
@@ -693,3 +750,3 @@ }); | ||
beforeEach(function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
@@ -739,3 +796,3 @@ node = new Funnel(inputPath); | ||
beforeEach(function() { | ||
var inputPath = path.join(fixturePath, 'dir1'); | ||
var inputPath = fixturePath + '/dir1'; | ||
@@ -742,0 +799,0 @@ node = new Funnel(inputPath); |
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
Filesystem access
Supply chain riskAccesses the file system, and could potentially read sensitive data.
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
46826
956
11
7
3
+ Addedfast-ordered-set@^1.0.0
+ Addedfs-tree-diff@0.3.0
+ Addedpath-posix@^1.0.0
+ Addedrimraf@^2.4.3
+ Addedblank-object@1.0.2(transitive)
+ Addedfast-ordered-set@1.0.3(transitive)
+ Addedfs-tree-diff@0.3.0(transitive)
+ Addedpath-posix@1.0.0(transitive)