broccoli-dependency-funnel
Advanced tools
Comparing version 1.1.0 to 2.0.0
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { | ||
value: true | ||
}); | ||
const fs = require('fs'); | ||
const path = require('path-posix'); | ||
const Plugin = require('broccoli-plugin'); | ||
const FSTree = require('fs-tree-diff'); | ||
const Entry = require('fs-tree-diff/lib/entry'); | ||
const existsSync = require('exists-sync'); | ||
const heimdall = require('heimdalljs'); | ||
const _logger = require('heimdalljs-logger'); | ||
const rimraf = require('rimraf'); | ||
const mrDepWalk = require('mr-dep-walk'); | ||
const copyFile = require('./utils/copy-file'); | ||
const existsStat = require('./utils/exists-stat'); | ||
const filterDirectory = require('./utils/filter-directory'); | ||
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })(); | ||
const logger = _logger('broccoli-dependency-funnel'); | ||
var _createClass = (function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ('value' in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; })(); | ||
var _get = function get(_x2, _x3, _x4) { var _again = true; _function: while (_again) { var object = _x2, property = _x3, receiver = _x4; _again = false; if (object === null) object = Function.prototype; var desc = Object.getOwnPropertyDescriptor(object, property); if (desc === undefined) { var parent = Object.getPrototypeOf(object); if (parent === null) { return undefined; } else { _x2 = parent; _x3 = property; _x4 = receiver; _again = true; desc = parent = undefined; continue _function; } } else if ('value' in desc) { return desc.value; } else { var getter = desc.get; if (getter === undefined) { return undefined; } return getter.call(receiver); } } }; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError('Cannot call a class as a function'); } } | ||
function _inherits(subClass, superClass) { if (typeof superClass !== 'function' && superClass !== null) { throw new TypeError('Super expression must either be null or a function, not ' + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } | ||
var _fs = require('fs'); | ||
var _fs2 = _interopRequireDefault(_fs); | ||
var _pathPosix = require('path-posix'); | ||
var _pathPosix2 = _interopRequireDefault(_pathPosix); | ||
var _broccoliPlugin = require('broccoli-plugin'); | ||
var _broccoliPlugin2 = _interopRequireDefault(_broccoliPlugin); | ||
var _fsTreeDiff = require('fs-tree-diff'); | ||
var _fsTreeDiff2 = _interopRequireDefault(_fsTreeDiff); | ||
var _fsTreeDiffLibEntry = require('fs-tree-diff/lib/entry'); | ||
var _fsTreeDiffLibEntry2 = _interopRequireDefault(_fsTreeDiffLibEntry); | ||
var _existsSync = require('exists-sync'); | ||
var _existsSync2 = _interopRequireDefault(_existsSync); | ||
var _heimdalljs = require('heimdalljs'); | ||
var _heimdalljs2 = _interopRequireDefault(_heimdalljs); | ||
var _heimdalljsLogger = require('heimdalljs-logger'); | ||
var _heimdalljsLogger2 = _interopRequireDefault(_heimdalljsLogger); | ||
var _rimraf = require('rimraf'); | ||
var _rimraf2 = _interopRequireDefault(_rimraf); | ||
var _mrDepWalk = require('mr-dep-walk'); | ||
var _mrDepWalk2 = _interopRequireDefault(_mrDepWalk); | ||
var _utilsCopyFile = require('./utils/copy-file'); | ||
var _utilsCopyFile2 = _interopRequireDefault(_utilsCopyFile); | ||
var _utilsExistsStat = require('./utils/exists-stat'); | ||
var _utilsExistsStat2 = _interopRequireDefault(_utilsExistsStat); | ||
var _utilsFilterDirectory = require('./utils/filter-directory'); | ||
var _utilsFilterDirectory2 = _interopRequireDefault(_utilsFilterDirectory); | ||
var logger = (0, _heimdalljsLogger2['default'])('broccoli-dependency-funnel'); | ||
function BroccoliDependencyFunnelSchema() { | ||
@@ -81,11 +27,6 @@ this.cacheHit = 0; | ||
var BroccoliDependencyFunnel = (function (_Plugin) { | ||
_inherits(BroccoliDependencyFunnel, _Plugin); | ||
function BroccoliDependencyFunnel(node) { | ||
var options = arguments.length <= 1 || arguments[1] === undefined ? {} : arguments[1]; | ||
_classCallCheck(this, BroccoliDependencyFunnel); | ||
_get(Object.getPrototypeOf(BroccoliDependencyFunnel.prototype), 'constructor', this).call(this, [node], { | ||
module.exports = class BroccoliDependencyFunnel extends Plugin { | ||
constructor(node, _options) { | ||
let options = _options || {}; | ||
super([node], { | ||
name: options.name, | ||
@@ -116,161 +57,144 @@ annotation: options.annotation, | ||
_createClass(BroccoliDependencyFunnel, [{ | ||
key: 'build', | ||
value: function build() { | ||
var node = _heimdalljs2['default'].start({ | ||
name: 'BroccoliDependencyFunnel (Build)', | ||
broccoliDependencyFunnel: true | ||
}, BroccoliDependencyFunnelSchema); | ||
var stats = node.stats; | ||
build() { | ||
let node = heimdall.start({ | ||
name: 'BroccoliDependencyFunnel (Build)', | ||
broccoliDependencyFunnel: true | ||
}, BroccoliDependencyFunnelSchema); | ||
let stats = node.stats; | ||
let inputPath = this.inputPaths[0]; | ||
var _inputPaths = _slicedToArray(this.inputPaths, 1); | ||
// Check for changes in the files included in the dependency graph | ||
if (this._depGraph) { | ||
let incomingDepGraphTree = this._getFSTree(this._depGraph); | ||
let depGraphPatch = this._depGraphTree.calculatePatch(incomingDepGraphTree); | ||
let hasDepGraphChanges = depGraphPatch.length !== 0; | ||
var inputPath = _inputPaths[0]; | ||
if (!hasDepGraphChanges) { | ||
let incomingNonDepGraphTree = this._getFSTree(this._nonDepGraph); | ||
let nonDepGraphPatch = this._nonDepGraphTree.calculatePatch(incomingNonDepGraphTree); | ||
let hasNonDepGraphChanges = nonDepGraphPatch.length !== 0; | ||
// Check for changes in the files included in the dependency graph | ||
if (this._depGraph) { | ||
var incomingDepGraphTree = this._getFSTree(this._depGraph); | ||
var depGraphPatch = this._depGraphTree.calculatePatch(incomingDepGraphTree); | ||
var hasDepGraphChanges = depGraphPatch.length !== 0; | ||
if (!hasDepGraphChanges) { | ||
var incomingNonDepGraphTree = this._getFSTree(this._nonDepGraph); | ||
var nonDepGraphPatch = this._nonDepGraphTree.calculatePatch(incomingNonDepGraphTree); | ||
var hasNonDepGraphChanges = nonDepGraphPatch.length !== 0; | ||
if (!hasNonDepGraphChanges) { | ||
stats.cacheHit++; | ||
logger.debug('cache hit, no changes'); | ||
node.stop(); | ||
return; | ||
} else if (this.include) { | ||
stats.cacheHit++; | ||
logger.debug('cache hit, no changes in dependency graph'); | ||
node.stop(); | ||
return; | ||
} else { | ||
stats.patchApplied++; | ||
logger.debug('applying patch', nonDepGraphPatch); | ||
_fsTreeDiff2['default'].applyPatch(inputPath, this.outputPath, nonDepGraphPatch); | ||
node.stop(); | ||
return; | ||
} | ||
if (!hasNonDepGraphChanges) { | ||
stats.cacheHit++; | ||
logger.debug('cache hit, no changes'); | ||
node.stop(); | ||
return; | ||
} else if (this.include) { | ||
stats.cacheHit++; | ||
logger.debug('cache hit, no changes in dependency graph'); | ||
node.stop(); | ||
return; | ||
} else { | ||
stats.patchApplied++; | ||
logger.debug('applying patch', nonDepGraphPatch); | ||
FSTree.applyPatch(inputPath, this.outputPath, nonDepGraphPatch); | ||
node.stop(); | ||
return; | ||
} | ||
} | ||
} | ||
var modules = []; | ||
let modules = []; | ||
var actualEntry = this.entry; | ||
var entryExists = (0, _existsSync2['default'])(_pathPosix2['default'].join(inputPath, actualEntry)); | ||
var usingModulesDir = false; | ||
let actualEntry = this.entry; | ||
let entryExists = existsSync(path.join(inputPath, actualEntry)); | ||
let usingModulesDir = false; | ||
// Ember-CLI might have all the modules inside a 'modules/' sub directory | ||
// so we check that as well | ||
if (!entryExists) { | ||
actualEntry = _pathPosix2['default'].join('modules', this.entry); | ||
entryExists = (0, _existsSync2['default'])(_pathPosix2['default'].join(inputPath, actualEntry)); | ||
usingModulesDir = true; | ||
} | ||
// Ember-CLI might have all the modules inside a 'modules/' sub directory | ||
// so we check that as well | ||
if (!entryExists) { | ||
actualEntry = path.join('modules', this.entry); | ||
entryExists = existsSync(path.join(inputPath, actualEntry)); | ||
usingModulesDir = true; | ||
} | ||
if (!entryExists) { | ||
stats.noEntry++; | ||
logger.debug('entry did not exist'); | ||
if (!entryExists) { | ||
stats.noEntry++; | ||
logger.debug('entry did not exist'); | ||
if (!this.include) { | ||
stats.copyAll++; | ||
logger.debug('copying all modules'); | ||
if (!this.include) { | ||
stats.copyAll++; | ||
logger.debug('copying all modules'); | ||
// TODO: We should just symlink to the inputPath to the outputPath | ||
modules = _fs2['default'].readdirSync(inputPath); | ||
this.copy(modules); | ||
} | ||
node.stop(); | ||
return; | ||
// TODO: We should just symlink to the inputPath to the outputPath | ||
modules = fs.readdirSync(inputPath); | ||
this.copy(modules); | ||
} | ||
var mrDepWalkNode = _heimdalljs2['default'].start({ | ||
name: 'BroccoliDependencyFunnel (Mr Dep Walk)' | ||
}); | ||
node.stop(); | ||
return; | ||
} | ||
modules = _mrDepWalk2['default'].depFilesFromFile(inputPath, { | ||
entry: this.entry, | ||
external: this.external || [], | ||
cwd: usingModulesDir ? 'modules' : '' | ||
}); | ||
let mrDepWalkNode = heimdall.start({ | ||
name: 'BroccoliDependencyFunnel (Mr Dep Walk)' | ||
}); | ||
// Ensure `this.entry` is included in `modules`. | ||
// | ||
// `this.entry` will already be included if a member of its dependency | ||
// graph depends back on it. If none of its dependencies depend on it, it | ||
// will not be included. | ||
// | ||
if (modules.indexOf(actualEntry) === -1) { | ||
modules.unshift(actualEntry); | ||
} | ||
mrDepWalkNode.stop(); | ||
stats.mrDepWalk++; | ||
logger.debug('Mr DepWalk executed'); | ||
modules = mrDepWalk.depFilesFromFile(inputPath, { | ||
entry: this.entry, | ||
external: this.external || [], | ||
cwd: usingModulesDir ? 'modules' : '' | ||
}); | ||
this._depGraph = modules.sort(); | ||
this._nonDepGraph = (0, _utilsFilterDirectory2['default'])(inputPath, '', function (module) { | ||
return modules.indexOf(module) === -1; | ||
}).sort(); | ||
// Ensure `this.entry` is included in `modules`. | ||
// | ||
// `this.entry` will already be included if a member of its dependency | ||
// graph depends back on it. If none of its dependencies depend on it, it | ||
// will not be included. | ||
// | ||
if (modules.indexOf(actualEntry) === -1) { | ||
modules.unshift(actualEntry); | ||
} | ||
mrDepWalkNode.stop(); | ||
stats.mrDepWalk++; | ||
logger.debug('Mr DepWalk executed'); | ||
_rimraf2['default'].sync(this.outputPath); | ||
this._depGraph = modules.sort(); | ||
this._nonDepGraph = filterDirectory(inputPath, '', function(module) { | ||
return modules.indexOf(module) === -1; | ||
}).sort(); | ||
var toCopy = this.include ? this._depGraph : this._nonDepGraph; | ||
rimraf.sync(this.outputPath); | ||
// TODO: should be patch based | ||
this.copy(toCopy); | ||
let toCopy = this.include ? this._depGraph : this._nonDepGraph; | ||
this._depGraphTree = this._getFSTree(this._depGraph); | ||
this._nonDepGraphTree = this._getFSTree(this._nonDepGraph); | ||
// TODO: should be patch based | ||
this.copy(toCopy); | ||
node.stop(); | ||
} | ||
}, { | ||
key: 'copy', | ||
value: function copy(inodes) { | ||
var inputPath = this.inputPaths[0]; | ||
var outputPath = this.outputPath; | ||
this._depGraphTree = this._getFSTree(this._depGraph); | ||
this._nonDepGraphTree = this._getFSTree(this._nonDepGraph); | ||
for (var i = 0; i < inodes.length; i++) { | ||
var _module2 = inodes[i]; | ||
(0, _utilsCopyFile2['default'])(_pathPosix2['default'].join(inputPath, _module2), _pathPosix2['default'].join(outputPath, _module2)); | ||
} | ||
} | ||
node.stop(); | ||
} | ||
/** | ||
* Constructs an FSTree from the passed in paths. | ||
* | ||
* @param {Array<String>} paths | ||
* @return {FSTree} | ||
*/ | ||
}, { | ||
key: '_getFSTree', | ||
value: function _getFSTree(paths) { | ||
var _inputPaths2 = _slicedToArray(this.inputPaths, 1); | ||
copy(inodes) { | ||
let inputPath = this.inputPaths[0]; | ||
let outputPath = this.outputPath; | ||
var inputPath = _inputPaths2[0]; | ||
for (let i = 0; i < inodes.length; i++) { | ||
let module = inodes[i]; | ||
copyFile(path.join(inputPath, module), path.join(outputPath, module)); | ||
} | ||
} | ||
var entries = paths.map(function (entryPath) { | ||
var absolutePath = _pathPosix2['default'].join(inputPath, entryPath); | ||
var stat = (0, _utilsExistsStat2['default'])(absolutePath); | ||
/** | ||
* Constructs an FSTree from the passed in paths. | ||
* | ||
* @param {Array<String>} paths | ||
* @return {FSTree} | ||
*/ | ||
_getFSTree(paths) { | ||
let inputPath = this.inputPaths[0]; | ||
let entries = paths.map(entryPath => { | ||
let absolutePath = path.join(inputPath, entryPath); | ||
let stat = existsStat(absolutePath); | ||
if (!stat) { | ||
return; | ||
} | ||
if (stat === null) { | ||
return; | ||
} | ||
return _fsTreeDiffLibEntry2['default'].fromStat(entryPath, stat); | ||
}).filter(Boolean); | ||
return Entry.fromStat(entryPath, stat); | ||
}).filter(Boolean); | ||
return _fsTreeDiff2['default'].fromEntries(entries); | ||
} | ||
}]); | ||
return BroccoliDependencyFunnel; | ||
})(_broccoliPlugin2['default']); | ||
exports['default'] = BroccoliDependencyFunnel; | ||
module.exports = exports['default']; | ||
return FSTree.fromEntries(entries); | ||
} | ||
}; |
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { | ||
value: true | ||
}); | ||
exports['default'] = copyFile; | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
const mkdirp = require('mkdirp'); | ||
const existsSync = require('exists-sync'); | ||
const symlinkOrCopy = require('symlink-or-copy'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
var _fs = require('fs'); | ||
var _fs2 = _interopRequireDefault(_fs); | ||
var _path = require('path'); | ||
var _path2 = _interopRequireDefault(_path); | ||
var _mkdirp = require('mkdirp'); | ||
var _mkdirp2 = _interopRequireDefault(_mkdirp); | ||
var _existsSync = require('exists-sync'); | ||
var _existsSync2 = _interopRequireDefault(_existsSync); | ||
var _symlinkOrCopy = require('symlink-or-copy'); | ||
var _symlinkOrCopy2 = _interopRequireDefault(_symlinkOrCopy); | ||
/** | ||
@@ -36,12 +15,11 @@ * Copies an input path to an output path by using the _copy | ||
*/ | ||
module.exports = function copyFile(sourcePath, destPath) { | ||
let destDir = path.dirname(destPath); | ||
function copyFile(sourcePath, destPath) { | ||
var destDir = _path2['default'].dirname(destPath); | ||
try { | ||
_symlinkOrCopy2['default'].sync(sourcePath, destPath); | ||
} catch (e) { | ||
symlinkOrCopy.sync(sourcePath, destPath); | ||
} catch(e) { | ||
// TODO: change mr-dep-walk API to expose found vs missing (aka external) deps | ||
// if sourcePath does not exist, do nothing | ||
if (!(0, _existsSync2['default'])(sourcePath)) { | ||
if (!existsSync(sourcePath)) { | ||
return; | ||
@@ -51,4 +29,4 @@ } | ||
// If it failed, make sure the directory exists | ||
if (!(0, _existsSync2['default'])(destDir)) { | ||
_mkdirp2['default'].sync(destDir); | ||
if (!existsSync(destDir)) { | ||
mkdirp.sync(destDir); | ||
} | ||
@@ -58,11 +36,9 @@ | ||
try { | ||
_fs2['default'].unlinkSync(destPath); | ||
} catch (e) { | ||
fs.unlinkSync(destPath); | ||
} catch(e) { | ||
// Continue regardless of error | ||
} | ||
_symlinkOrCopy2['default'].sync(sourcePath, destPath); | ||
symlinkOrCopy.sync(sourcePath, destPath); | ||
} | ||
} | ||
module.exports = exports['default']; | ||
}; |
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { | ||
value: true | ||
}); | ||
exports['default'] = existsStat; | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
var _fs = require('fs'); | ||
var _fs2 = _interopRequireDefault(_fs); | ||
const fs = require('fs'); | ||
/** | ||
@@ -21,7 +11,5 @@ * Stats a path. If it exists, it returns the stat information. Otherwise it | ||
*/ | ||
function existsStat(path) { | ||
module.exports = function existsStat(path) { | ||
try { | ||
var stat = _fs2['default'].statSync(path); | ||
return stat; | ||
return fs.statSync(path); | ||
} catch (e) { | ||
@@ -31,3 +19,1 @@ return null; | ||
} | ||
module.exports = exports['default']; |
'use strict'; | ||
Object.defineProperty(exports, '__esModule', { | ||
value: true | ||
}); | ||
exports['default'] = filterDirectory; | ||
const fs = require('fs'); | ||
const path = require('path'); | ||
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } | ||
function _toConsumableArray(arr) { if (Array.isArray(arr)) { for (var i = 0, arr2 = Array(arr.length); i < arr.length; i++) arr2[i] = arr[i]; return arr2; } else { return Array.from(arr); } } | ||
var _fs = require('fs'); | ||
var _fs2 = _interopRequireDefault(_fs); | ||
var _path = require('path'); | ||
var _path2 = _interopRequireDefault(_path); | ||
/** | ||
@@ -28,15 +14,14 @@ * Filter the files in a directory that match a given predicate. | ||
*/ | ||
module.exports = function filterDirectory(directory, prefix, predicate) { | ||
let inodes = fs.readdirSync(directory); | ||
let files = []; | ||
function filterDirectory(directory, prefix, predicate) { | ||
var inodes = _fs2['default'].readdirSync(directory); | ||
var files = []; | ||
for (let i = 0; i < inodes.length; i++) { | ||
let inode = inodes[i]; | ||
let fullPath = path.join(directory, inode); | ||
let currentPath = path.join(prefix, inode); | ||
let isDirectory = fs.statSync(fullPath).isDirectory(); | ||
for (var i = 0; i < inodes.length; i++) { | ||
var inode = inodes[i]; | ||
var fullPath = _path2['default'].join(directory, inode); | ||
var currentPath = _path2['default'].join(prefix, inode); | ||
var isDirectory = _fs2['default'].statSync(fullPath).isDirectory(); | ||
if (isDirectory) { | ||
files.push.apply(files, _toConsumableArray(filterDirectory(fullPath, currentPath, predicate))); | ||
files.push.apply(files, filterDirectory(fullPath, currentPath, predicate)); | ||
} else if (predicate(currentPath)) { | ||
@@ -48,4 +33,2 @@ files.push(currentPath); | ||
return files; | ||
} | ||
module.exports = exports['default']; | ||
}; |
{ | ||
"name": "broccoli-dependency-funnel", | ||
"version": "1.1.0", | ||
"version": "2.0.0", | ||
"description": "Funnels a set of files included (or excluded) from a JS dependency graph", | ||
"main": "dist/index.js", | ||
"main": "src/index.js", | ||
"scripts": { | ||
"build": "rm -rf dist && broccoli build dist", | ||
"build:serve": "broccoli serve", | ||
"test": "npm run build && mocha dist/tests --reporter tap", | ||
"test:lint": "npm run build && mocha dist/tests/lint.js", | ||
"test:debug": "npm run build && mocha debug dist/tests", | ||
"test:watch": "testem", | ||
"prepublish": "npm run build" | ||
"test": "mocha tests --reporter tap", | ||
"test:lint": " mocha tests/lint.js", | ||
"test:debug": "build && mocha debug tests", | ||
"test:watch": "testem" | ||
}, | ||
"engines": { | ||
"node": ">= 4.0.0" | ||
}, | ||
"keywords": [ | ||
@@ -39,10 +39,5 @@ "broccoli", | ||
"broccoli": "^0.16.9", | ||
"broccoli-babel-transpiler": "^5.6.0", | ||
"broccoli-cli": "^1.0.0", | ||
"broccoli-lint-eslint": "^2.7.0", | ||
"broccoli-merge-trees": "^1.1.4", | ||
"broccoli-stew": "^1.3.2", | ||
"chai": "^3.5.0", | ||
"chai-files": "^1.4.0", | ||
"es6-promise": "^3.2.1", | ||
"co": "^4.6.0", | ||
"fixturify": "^0.3.0", | ||
@@ -52,3 +47,2 @@ "fs-extra": "^0.30.0", | ||
"mocha-eslint": "^2.1.1", | ||
"regenerator-runtime": "^0.9.5", | ||
"testem": "^1.13.0", | ||
@@ -55,0 +49,0 @@ "walk-sync": "^0.3.1" |
# broccoli-dependency-funnel | ||
[![Build Status](https://travis-ci.org/ember-engines/broccoli-dependency-funnel.svg?branch=master)](https://travis-ci.org/ember-engines/broccoli-dependency-funnel) | ||
[![Build Status](https://ci.appveyor.com/api/projects/status/ilvo74csww13f3j3?svg=true)](https://ci.appveyor.com/project/embercli/broccoli-dependency-funnel) | ||
@@ -5,0 +6,0 @@ This [Broccoli](https://github.com/broccolijs/broccoli) plugin funnels a set of files included (or excluded) from a JS dependency graph. |
New author
Supply chain riskA new npm collaborator published a version of the package for the first time. New collaborators are usually benign additions to a project, but do indicate a change to the security surface area of a package.
Found 1 instance in 1 package
10
46
11282
242
1