-
Notifications
You must be signed in to change notification settings - Fork 5
/
index.js
141 lines (127 loc) · 4.79 KB
/
index.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
'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
}