UNPKG

lasso

Version:

Lasso.js is a build tool and runtime library for building and bundling all of the resources needed by a web application

164 lines (138 loc) 5.36 kB
const dependencyWalker = require('../dependency-walker'); const DependencyList = require('../DependencyList'); const thresholdRegex = /^(\d+)([%]*)$/; const hasOwn = Object.prototype.hasOwnProperty; function onDependency (tracking, strictIntersection, firstSet, i) { return function (dependency, context) { if (dependency.isPackageDependency()) { return; } const key = dependency.getKey(); const info = tracking[key]; if (info === undefined) { tracking[key] = { dependency, count: 1 }; } else { info.count++; } if ((i === 0) && strictIntersection) { // strict intersection so only need to keep track // dependencies from first set (which is a little // arbitrary but will work) firstSet.push(dependency); } }; } module.exports = { properties: { dependencies: 'array', threshold: 'object' }, async init (lassoContext) { if (!this.dependencies) { throw new Error('"dependencies" property is required'); } if (!Array.isArray(this.dependencies)) { throw new Error('"dependencies" property is required'); } this.dependencies = new DependencyList( this.dependencies, lassoContext.dependencyRegistry, this.getParentManifestDir(), this.getParentManifestPath()); if (this.threshold) { if (typeof this.threshold === 'string') { const match = thresholdRegex.exec(this.threshold); let units; if (!match || ((units = match[2]) && (units !== '%'))) { throw new Error('Invalid threshold: ' + this.threshold); } this.threshold = { value: parseInt(match[1], 10), units }; } else { this.threshold = { value: this.threshold }; } } }, getDir: function() { return null; }, async getDependencies (lassoContext) { const tracking = {}; const flags = lassoContext.flags; const firstSet = []; const dependencies = await this.dependencies.normalize(); const numDependencies = dependencies.length; let thresholdValue; if (this.threshold) { thresholdValue = this.threshold.value; if (this.threshold.units === '%') { // A dependency will become part of the intersection if it is in at X percent of the enumerated list of dependencies thresholdValue = thresholdValue / 100 * numDependencies; } else { // A dependency will become part of the intersection if it is in at least X of the enumerated list of dependencies thresholdValue = this.threshold.value; } } else { // strict intersection -- only include the dependencies that are in the enumerated list of dependencies thresholdValue = numDependencies; } const strictIntersection = (thresholdValue >= numDependencies); for (const [i, dependency] of dependencies.entries()) { // HACK: The built-in `dep-require` dependency type // uses its `Deduper` instance to ignore dependencies // within the same "phase" of a lasso operation. // // However, for the purposes of calculating intersection // we should not de-duplicate across each "walk" of // starting dependency. // // The `Deduper` stores a cache of "visited" dependencies in // `lassoContext.phaseData['dependency-require']`. // // We reset the `phaseData` property to remove this // cache before we walk each starting dependency. const oldPhaseData = lassoContext.phaseData; lassoContext.phaseData = {}; await dependencyWalker.walk({ lassoContext, dependency, flags, on: { dependency: onDependency(tracking, strictIntersection, firstSet, i) } }); lassoContext.phaseData = oldPhaseData; } const intersection = []; function checkDependency(info) { if (info.count >= thresholdValue) { intersection.push(info.dependency); } } if (strictIntersection) { // strict intersection for (let i = 0, len = firstSet.length; i < len; i++) { const dependency = firstSet[i]; checkDependency(tracking[dependency.getKey()]); } } else { // not a strict intersection so we need to check counts for all dependencies for (const key in tracking) { if (hasOwn.call(tracking, key)) { checkDependency(tracking[key]); } } } return intersection; }, calculateKey () { return null; // A just use a unique ID for this dependency } };