Socket
Socket
Sign inDemoInstall

broccoli-filter

Package Overview
Dependencies
Maintainers
3
Versions
18
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

broccoli-filter - npm Package Compare versions

Comparing version 1.2.1 to 1.2.2

lib/cache.js

4

CHANGELOG.md
# master
# 1.2.0
* Replace with @caitp's cauliflower-filter implementation and @stefanpenner's tests
# 1.1.0

@@ -4,0 +8,0 @@

275

index.js

@@ -1,138 +0,205 @@

var fs = require('fs')
var path = require('path')
var mkdirp = require('mkdirp')
var Promise = require('rsvp').Promise
var Plugin = require('broccoli-plugin')
var helpers = require('broccoli-kitchen-sink-helpers')
var walkSync = require('walk-sync')
var mapSeries = require('promise-map-series')
var symlinkOrCopySync = require('symlink-or-copy').sync
'use strict';
var fs = require('fs');
var path = require('path');
var mkdirp = require('mkdirp');
var Promise = require('rsvp').Promise;
var Plugin = require('broccoli-plugin');
var helpers = require('broccoli-kitchen-sink-helpers');
var walkSync = require('walk-sync');
var mapSeries = require('promise-map-series');
var symlinkOrCopySync = require('symlink-or-copy').sync;
var copyDereferenceSync = require('copy-dereference').sync;
var Cache = require('./lib/cache');
var debugGenerator = require('debug');
var keyForFile = require('./lib/key-for-file');
module.exports = Filter
Filter.prototype = Object.create(Plugin.prototype)
Filter.prototype.constructor = Filter
function Filter (inputNode, options) {
if (!inputNode) {
throw new Error('broccoli-filter must be passed an inputNode, instead it received `undefined`');
module.exports = Filter;
Filter.prototype = Object.create(Plugin.prototype);
Filter.prototype.constructor = Filter;
function Filter(inputTree, options) {
if (!this || !(this instanceof Filter) ||
Object.getPrototypeOf(this) === Filter.prototype) {
throw new TypeError('Filter is an abstract class and must be sub-classed');
}
options = options || {}
Plugin.call(this, [inputNode], {
name: options.name,
annotation: options.annotation
})
if (options.extensions != null) this.extensions = options.extensions
if (options.targetExtension != null) this.targetExtension = options.targetExtension
if (options.inputEncoding !== undefined) this.inputEncoding = options.inputEncoding
if (options.outputEncoding !== undefined) this.outputEncoding = options.outputEncoding
var name = 'broccoli-filter:' + (this.constructor.name);
if (this.description) {
name += ' > [' + this.description + ']';
}
this._debug = debugGenerator(name);
Plugin.call(this, [inputTree]);
/* Destructuring assignment in node 0.12.2 would be really handy for this! */
if (options) {
if (options.extensions != null)
this.extensions = options.extensions;
if (options.targetExtension != null)
this.targetExtension = options.targetExtension;
if (options.inputEncoding != null)
this.inputEncoding = options.inputEncoding;
if (options.outputEncoding != null)
this.outputEncoding = options.outputEncoding;
}
this._cache = new Cache();
this._canProcessCache = Object.create(null);
this._destFilePathCache = Object.create(null);
}
Filter.prototype.build = function (readTree, destDir) {
var self = this
var srcDir = this.inputPaths[0]
var destDir = this.outputPath
Filter.prototype.build = function build() {
var self = this;
var srcDir = this.inputPaths[0];
var destDir = this.outputPath;
var paths = walkSync(srcDir);
var paths = walkSync(srcDir)
this._cache.deleteExcept(paths).forEach(function(key) {
fs.unlinkSync(this.cachePath + '/' + key);
}, this);
return mapSeries(paths, function (relativePath) {
return mapSeries(paths, function rebuildEntry(relativePath) {
var destPath = destDir + '/' + relativePath;
if (relativePath.slice(-1) === '/') {
mkdirp.sync(destDir + '/' + relativePath)
mkdirp.sync(destPath);
} else {
if (self.canProcessFile(relativePath)) {
return self.processAndCacheFile(srcDir, destDir, relativePath)
return self.processAndCacheFile(srcDir, destDir, relativePath);
} else {
symlinkOrCopySync(srcDir + '/' + relativePath, destDir + '/' + relativePath)
var srcPath = srcDir + '/' + relativePath;
symlinkOrCopySync(srcPath, destPath);
}
}
})
}
});
};
Filter.prototype.canProcessFile = function (relativePath) {
return this.getDestFilePath(relativePath) != null
}
Filter.prototype.canProcessFile =
function canProcessFile(relativePath) {
return !!this.getDestFilePath(relativePath);
};
Filter.prototype.getDestFilePath = function (relativePath) {
for (var i = 0; i < this.extensions.length; i++) {
var ext = this.extensions[i]
Filter.prototype.getDestFilePath = function getDestFilePath(relativePath) {
if (this.extensions == null) return relativePath;
for (var i = 0, ii = this.extensions.length; i < ii; ++i) {
var ext = this.extensions[i];
if (relativePath.slice(-ext.length - 1) === '.' + ext) {
if (this.targetExtension != null) {
relativePath = relativePath.slice(0, -ext.length) + this.targetExtension
relativePath =
relativePath.slice(0, -ext.length) + this.targetExtension;
}
return relativePath
return relativePath;
}
}
return null
return null;
}
Filter.prototype.processAndCacheFile = function (srcDir, destDir, relativePath) {
var self = this
Filter.prototype.processAndCacheFile =
function processAndCacheFile(srcDir, destDir, relativePath) {
var self = this;
var cacheEntry = this._cache.get(relativePath);
var outputRelativeFile = self.getDestFilePath(relativePath);
this._cache = this._cache || {}
this._cacheIndex = this._cacheIndex || 0
var cacheEntry = this._cache[relativePath]
if (cacheEntry != null && cacheEntry.hash === hash(cacheEntry.inputFiles)) {
symlinkOrCopyFromCache(cacheEntry)
if (cacheEntry) {
var hashResult = hash(srcDir, cacheEntry.inputFile);
if (cacheEntry.hash.hash === hashResult.hash) {
this._debug('cache hit: %s', relativePath);
return symlinkOrCopyFromCache(cacheEntry, destDir, outputRelativeFile);
} else {
this._debug('cache miss: %s \n - previous: %o \n - next: %o ', relativePath, cacheEntry.hash.key, hashResult.key);
}
} else {
return Promise.resolve()
.then(function () {
return self.processFile(srcDir, destDir, relativePath)
})
.catch(function (err) {
// Augment for helpful error reporting
err.file = relativePath
err.treeDir = srcDir
throw err
})
.then(function (cacheInfo) {
copyToCache(cacheInfo)
})
this._debug('cache prime: %s', relativePath);
}
function hash (filePaths) {
return filePaths.map(function (filePath) {
return helpers.hashTree(srcDir + '/' + filePath)
}).join(',')
}
return Promise.resolve().
then(function asyncProcessFile() {
return self.processFile(srcDir, destDir, relativePath);
}).
then(copyToCache,
// TODO(@caitp): error wrapper is for API compat, but is not particularly
// useful.
// istanbul ignore next
function asyncProcessFileErrorWrapper(e) {
if (typeof e !== 'object') e = new Error('' + e);
e.file = relativePath;
e.treeDir = srcDir;
throw e;
})
function symlinkOrCopyFromCache (cacheEntry) {
for (var i = 0; i < cacheEntry.outputFiles.length; i++) {
var dest = destDir + '/' + cacheEntry.outputFiles[i]
mkdirp.sync(path.dirname(dest))
symlinkOrCopySync(self.cachePath + '/' + cacheEntry.cacheFiles[i], dest)
function copyToCache() {
var entry = {
hash: hash(srcDir, relativePath),
inputFile: relativePath,
outputFile: destDir + '/' + outputRelativeFile,
cacheFile: self.cachePath + '/' + outputRelativeFile
};
if (fs.existsSync(entry.cacheFile)) {
fs.unlinkSync(entry.cacheFile);
} else {
mkdirp.sync(path.dirname(entry.cacheFile));
}
copyDereferenceSync(entry.outputFile, entry.cacheFile);
return self._cache.set(relativePath, entry);
}
};
function copyToCache (cacheInfo) {
var cacheEntry = {
inputFiles: (cacheInfo || {}).inputFiles || [relativePath],
outputFiles: (cacheInfo || {}).outputFiles || [self.getDestFilePath(relativePath)],
cacheFiles: []
}
for (var i = 0; i < cacheEntry.outputFiles.length; i++) {
var cacheFile = (self._cacheIndex++) + ''
cacheEntry.cacheFiles.push(cacheFile)
Filter.prototype.processFile =
function processFile(srcDir, destDir, relativePath) {
var self = this;
var inputEncoding = this.inputEncoding;
var outputEncoding = this.outputEncoding;
if (inputEncoding === void 0) inputEncoding = 'utf8';
if (outputEncoding === void 0) outputEncoding = 'utf8';
var contents = fs.readFileSync(
srcDir + '/' + relativePath, { encoding: inputEncoding });
helpers.copyPreserveSync(
destDir + '/' + cacheEntry.outputFiles[i],
self.cachePath + '/' + cacheFile)
}
cacheEntry.hash = hash(cacheEntry.inputFiles)
self._cache[relativePath] = cacheEntry
}
return Promise.resolve(this.processString(contents, relativePath)).
then(function asyncOutputFilteredFile(outputString) {
var outputPath = self.getDestFilePath(relativePath);
if (outputPath == null) {
throw new Error('canProcessFile("' + relativePath + '") is true, but getDestFilePath("' + relativePath + '") is null');
}
outputPath = destDir + '/' + outputPath;
mkdirp.sync(path.dirname(outputPath));
fs.writeFileSync(outputPath, outputString, {
encoding: outputEncoding
});
});
};
Filter.prototype.processString =
function unimplementedProcessString(contents, relativePath) {
throw new Error(
'When subclassing broccoli-filter you must implement the ' +
'`processString()` method.');
};
function hash(src, filePath) {
var path = src + '/' + filePath;
var key = keyForFile(path);
return {
key: key,
hash: helpers.hashStrings([
path,
key.size,
key.mode,
key.mtime
])
};
}
Filter.prototype.processFile = function (srcDir, destDir, relativePath) {
var self = this
var inputEncoding = (this.inputEncoding === undefined) ? 'utf8' : this.inputEncoding
var outputEncoding = (this.outputEncoding === undefined) ? 'utf8' : this.outputEncoding
var string = fs.readFileSync(srcDir + '/' + relativePath, { encoding: inputEncoding })
return Promise.resolve(self.processString(string, relativePath))
.then(function (outputString) {
var outputPath = self.getDestFilePath(relativePath)
fs.writeFileSync(destDir + '/' + outputPath, outputString, { encoding: outputEncoding })
})
function symlinkOrCopyFromCache(entry, dest, relativePath) {
mkdirp.sync(path.dirname(entry.outputFile));
symlinkOrCopySync(entry.cacheFile, dest + '/' + relativePath);
}
Filter.prototype.processString = function() {
throw new Error('When subclassing broccoli-filter you must implement the `processString()` method.');
};
{
"name": "broccoli-filter",
"description": "Helper base class for Broccoli plugins that map input files into output files one-to-one",
"version": "1.2.1",
"author": "Jo Liss <joliss42@gmail.com>",
"version": "1.2.2",
"author": [
"Jo Liss <joliss42@gmail.com>",
"Caitlin Potter <caitpotter88@gmail.com>"
],
"main": "index.js",
"scripts": {
"test": "mocha",
"test:debug": "mocha debug",
"test:debug:brk": "mocha --debug-brk",
"test:watch": "mocha --watch",
"test:coverage": "istanbul cover --config=test/istanbul.yml _mocha"
},
"license": "MIT",

@@ -18,10 +28,24 @@ "repository": {

"dependencies": {
"broccoli-kitchen-sink-helpers": "^0.2.6",
"broccoli-kitchen-sink-helpers": "^0.2.7",
"broccoli-plugin": "^1.0.0",
"mkdirp": "^0.3.5",
"copy-dereference": "^1.0.0",
"debug": "^2.2.0",
"mkdirp": "^0.5.1",
"promise-map-series": "^0.2.1",
"rsvp": "^3.0.16",
"rsvp": "^3.0.18",
"symlink-or-copy": "^1.0.1",
"walk-sync": "^0.1.3"
},
"devDependencies": {
"broccoli": "^0.16.3",
"broccoli-test-helpers": "^0.0.8",
"chai": "^3.0.0",
"chai-as-promised": "^5.1.0",
"coveralls": "^2.11.4",
"istanbul": "^0.3.17",
"minimatch": "^2.0.8",
"mocha": "^2.2.5",
"rimraf": "^2.4.2",
"sinon": "^1.15.3"
}
}
# broccoli-filter
[![Build Status](https://travis-ci.org/broccolijs/broccoli-filter.svg?branch=master)](https://travis-ci.org/broccolijs/broccoli-filter)
[![Build status](https://ci.appveyor.com/api/projects/status/hc68s0vbn9di4ehi/branch/master?svg=true)](https://ci.appveyor.com/project/joliss/broccoli-filter/branch/master)
Helper base class for Broccoli plugins that map input files into output files

@@ -27,24 +30,12 @@ one-to-one.

/**
* Virtual method `canProcessFile`: determine whether hard processing is used
* to move the source file into the output directory, or whether a simple
* symlinking approach should be sufficient.
* Virtual method `getDestFilePath`: determine whether the source file should
* be processed, and optionally rename the output file when processing occurs.
*
* By default, this method returns true if the `extensions` option is a
* non-empty list, and the relativePath ends with one of the extensions.
* Return `null` to pass the file through without processing. Return
* `relativePath` to process the file with `processString`. Return a
* different path to process the file with `processString` and rename it.
*
* The results of this operation are cached automatically, and it will not be
* invoked again for a given relative path.
*/
virtual canProcessFile(relativePath: string): boolean;
/**
* Virtual method `getDestFilePath`: optionally rename the output file when
* processing occurs.
*
* By default, if the options passed into the Filter constructor contains a
* By default, if the options passed into the `Filter` constructor contain a
* property `extensions`, and `targetExtension` is supplied, the first matching
* extension in the list is replaced with the `targetExtension` option's value.
*
* The results of this operation are cached automatically, and it will not be
* invoked again for a given relative path.
*/

@@ -71,4 +62,3 @@ virtual getDestFilePath(relativePath: string): string;

All options except `name` and `annotation` can also be set on the prototype
instead of being passed into the constructor. <!-- This was a regrettable
choice. -->
instead of being passed into the constructor.

@@ -75,0 +65,0 @@ ### Example Usage

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc