'use strict' // This list of [feature, augmentationFunction] pairs is used to maintain // backwards compatibility with older broccoli-plugin versions. // // If a plugin doesn't support `feature`, then `augmentationFunction` is // called on its node info (as returned by node.__broccoliGetInfo__()) // in order to bring the interface up-to-date. If a plugin is missing several // features, each `augmentationFunction` is applied in succession. // // Add new features to the bottom of the list. var augmenters = [ [ 'persistentOutputFlag', function(nodeInfo) { nodeInfo.persistentOutput = false } ], [ 'sourceDirectories', function(nodeInfo) { nodeInfo.nodeType = 'transform' } ], [ 'needsCacheFlag', function(nodeInfo) { if (nodeInfo.nodeType === 'transform') { nodeInfo.needsCache = true } } ], [ 'volatileFlag', function(nodeInfo) { if (nodeInfo.nodeType === 'transform') { nodeInfo.volatile = false } } ], [ 'trackInputChangesFlag', function(nodeInfo) { if (nodeInfo.nodeType === 'transform') { nodeInfo.trackInputChanges = false } } ], [ 'fsFacadeFlag', function (nodeInfo) { if (nodeInfo.nodeType === 'transform') { nodeInfo.fsFacade = false } } ] ] var features = {} for (var i = 0; i < augmenters.length; i++) { features[augmenters[i][0]] = true } exports.features = features exports.getNodeInfo = getNodeInfo function getNodeInfo(node) { // Check that `node` is in fact a Broccoli node if (node == null || !node.__broccoliGetInfo__) { if (typeof node === 'string') { throw new InvalidNodeError('"' + node + '": String nodes are not supported. Use the WatchedDir class provided by the broccoli-source package instead.') } else if (node != null && (typeof node.read === 'function' || typeof node.rebuild === 'function')) { var legacyNodeDescription = node.description || node.constructor && node.constructor.name || ('' + node) throw new InvalidNodeError( legacyNodeDescription + ': The .read/.rebuild API is no longer supported as of Broccoli 1.0. ' + 'Plugins must now derive from broccoli-plugin. ' + 'https://github.com/broccolijs/broccoli/blob/master/docs/broccoli-1-0-plugin-api.md') } else { throw new InvalidNodeError(node + ' is not a Broccoli node') } } // Call __broccoliGetInfo__. Note that we're passing the builder's full // feature set (the `features` variable) rather than the smaller feature set // we'll be mimicking to interact with this plugin. This is a fairly // arbitrary choice, but it's easier to implement, and it usually won't make // a difference because the Plugin class won't care about features it // doesn't know about. var originalNodeInfo = node.__broccoliGetInfo__(features) // Now, for backward compatibility, deal with plugins that don't implement // our full feature set: // 1. Make a shallow copy of the nodeInfo hash so we can modify it // // We don't to use prototypal inheritance (Object.create) because some test // code can get confused if hasOwnProperty isn't true. var nodeInfo = { // lazily return the original instantiation stack // this avoids eagerly accessing the stacks, which is costly get instantiationStack() { return originalNodeInfo.instantiationStack; } }; for (var key in originalNodeInfo) { if (key === 'instantiationStack') { continue; } nodeInfo[key] = originalNodeInfo[key] } // 2. Discover features we have in common for (var i = 0; i < augmenters.length; i++) { var feature = augmenters[i][0] if (!node.__broccoliFeatures__[feature]) { break } } // 3. Augment the interface with the new features that the plugin doesn't support for (; i < augmenters.length; i++) { var fn = augmenters[i][1] fn(nodeInfo) } // We generally trust the nodeInfo to be valid, but unexpected // nodeTypes could break our code paths really badly, and some of those // paths call rimraf, so we check this to be safe. if (nodeInfo.nodeType !== 'transform' && nodeInfo.nodeType !== 'source') { throw new InvalidNodeError('Unexpected nodeType: ' + nodeInfo.nodeType) } return nodeInfo } exports.InvalidNodeError = InvalidNodeError InvalidNodeError.prototype = Object.create(Error.prototype) InvalidNodeError.prototype.constructor = InvalidNodeError function InvalidNodeError(message) { // Subclassing Error in ES5 is non-trivial because reasons, so we need this // extra constructor logic from http://stackoverflow.com/a/17891099/525872. var temp = Error.apply(this, arguments) // Need to assign temp.name for correct error class in .stack and .message temp.name = this.name = this.constructor.name this.stack = temp.stack this.message = temp.message }