1 | # mako-tree
|
2 |
|
3 | > The build tree structure used internally by [mako](https://github.com/makojs/core)
|
4 |
|
5 | [![npm version](https://img.shields.io/npm/v/mako-tree.svg)](https://www.npmjs.com/package/mako-tree)
|
6 | [![npm dependencies](https://img.shields.io/david/makojs/tree.svg)](https://david-dm.org/makojs/tree)
|
7 | [![npm dev dependencies](https://img.shields.io/david/dev/makojs/tree.svg)](https://david-dm.org/makojs/tree#info=devDependencies)
|
8 | [![build status](https://img.shields.io/travis/makojs/tree.svg)](https://travis-ci.org/makojs/tree)
|
9 |
|
10 | ## Overview
|
11 |
|
12 | When working with mako build hooks, the first 2 arguments will be the current `file` and the build
|
13 | `tree` respectively. Currently, both of those APIs are contained in this module, as they tightly
|
14 | coupled and don't make much sense on their own. (at least at the current time)
|
15 |
|
16 | Throughout the "analyze" phase, a tree is being built up, starting from the list of entry files.
|
17 | Each file being processed adds any direct dependencies, which will then recursively be processed
|
18 | to find more dependencies. Each vertex in the graph corresponds to some sort of input file.
|
19 |
|
20 | During the "build" phase, the tree _may_ be trimmed down, such as the case where the entire
|
21 | dependency chain for a JS file will be combined into a single output file. By the end of the build,
|
22 | each vertex in the graph corresponds to an output file.
|
23 |
|
24 | ## API
|
25 |
|
26 | > As mako continues to be developed and evolve, some documentation and guides dedicated to plugin
|
27 | authors will surface. For now, the following is purely the API available to both the `file` and
|
28 | `tree` parameters in plugins/hooks.
|
29 |
|
30 | The `Tree` constructor (documented below) is the primary export for the module. It must be used
|
31 | with the `new` keyword.
|
32 |
|
33 | ```js
|
34 | var Tree = require('mako-tree');
|
35 | var tree = new Tree();
|
36 | ```
|
37 |
|
38 | ### Tree() *(constructor)*
|
39 |
|
40 | Each instance represents a build tree. Internally, a graph is used to manage the relationships
|
41 | between all the files being tracked.
|
42 |
|
43 | **NOTE:** All paths are assumed to be absolute, this library makes no attempt to set a base/root
|
44 | directory and maintain relative paths.
|
45 |
|
46 | ### Tree#hasFile(location)
|
47 |
|
48 | Returns a `Boolean` reflecting if the file at the given `location` exists in this tree.
|
49 |
|
50 | ### Tree#addFile(location)
|
51 |
|
52 | Adds a file at the given `location` to the tree, if it is not already present, and returns the
|
53 | corresponding `File` instance.
|
54 |
|
55 | ### Tree#getFile(location)
|
56 |
|
57 | Returns the `File` instance for the file at the given `location`. It is assumed to already be part
|
58 | of the graph, and will throw an error if not found.
|
59 |
|
60 | ### Tree#getFiles([options])
|
61 |
|
62 | Returns an `Array` of all the files in this graph.
|
63 |
|
64 | If `options.topological` is set, the returned list will be in
|
65 | [topological order](https://en.wikipedia.org/wiki/Topological_sorting), which respects all
|
66 | dependencies so processing is safe where order matters.
|
67 |
|
68 | If `options.objects` is set, the returned list will be `File` objects.
|
69 |
|
70 | ### Tree#removeFile(location)
|
71 |
|
72 | Removes the file at the given `location` from the tree. To successfully remove a file, it must not
|
73 | be depended on by another file. This is mostly a plumbing function, and plugin authors are likely
|
74 | going to use `removeDependency()` instead.
|
75 |
|
76 | ### Tree#isSource(location)
|
77 |
|
78 | Returns a `Boolean` telling whether or not the file at `location` is an entry file. (in other
|
79 | words, is not a dependency)
|
80 |
|
81 | ### Tree#getEntries([options])
|
82 |
|
83 | Returns an `Array` of all the entry files in this graph. (in other words, files that are at the
|
84 | end of the dependency chains)
|
85 |
|
86 | If `options.from` is set, the returned list will only include entries that are reachable from that
|
87 | specified file.
|
88 |
|
89 | If `options.objects` is set, the returned list will be `File` objects.
|
90 |
|
91 | ### Tree#hasDependency(parent, child)
|
92 |
|
93 | Returns a `Boolean` reflecting if the dependency relationship between `parent` and `child` already
|
94 | exists in the tree.
|
95 |
|
96 | ### Tree#addDependency(parent, child)
|
97 |
|
98 | Adds a new dependency relationship to the graph setting `parent` as depending on `child`.
|
99 |
|
100 | If `child` is not already part of the tree, it will be added. However, if `parent` is not in the
|
101 | tree, that is assumed to be an error.
|
102 |
|
103 | This will return the `File` instance for the `child` file.
|
104 |
|
105 | ### Tree#removeDependency(parent, child)
|
106 |
|
107 | Removes the specified dependency relationship, basically saying that `parent` no longer depends on
|
108 | `child`.
|
109 |
|
110 | ### Tree#dependenciesOf(file, [options])
|
111 |
|
112 | Returns an `Array` of files that are dependencies of the given `file`.
|
113 |
|
114 | By default, it will only return the direct descendants, but setting `options.recursive` will return
|
115 | a flat list of all the files **down** the entire dependency chain.
|
116 |
|
117 | If `options.objects` is set, the returned list will be `File` objects.
|
118 |
|
119 | ### Tree#hasDependant(child, parent)
|
120 |
|
121 | Returns a `Boolean` reflecting if the dependency relationship between `child` and `parent` already
|
122 | exists in the tree.
|
123 |
|
124 | ### Tree#addDependant(child, parent)
|
125 |
|
126 | Adds a new dependency relationship to the graph setting `child` as depended on by `parent`.
|
127 |
|
128 | If `parent` is not already part of the tree, it will be added. However, if `child` is not in the
|
129 | tree, that is assumed to be an error.
|
130 |
|
131 | This will return the `File` instance for the `parent` file.
|
132 |
|
133 | ### Tree#removeDependant(child, parent)
|
134 |
|
135 | Removes the specified dependency relationship, basically saying that `child` no longer is depended
|
136 | on by `parent`.
|
137 |
|
138 | ### Tree#dependantsOf(file, [options])
|
139 |
|
140 | Returns an `Array` of files that depend on the given `file`.
|
141 |
|
142 | By default, it will only return the direct ancestors, but adding `options.recursive` will return a
|
143 | flat list of all the files **up** the entire dependency chain.
|
144 |
|
145 | If `options.objects` is set, the returned list will be `File` objects.
|
146 |
|
147 | ### Tree#timing()
|
148 |
|
149 | Aggregates all the timers for all files in the tree. These stats are useful when trying to figure
|
150 | out which plugins/hooks are taking up the most time so they can hopefully be optimized.
|
151 |
|
152 | ### Tree#size()
|
153 |
|
154 | Returns the number of files in the tree.
|
155 |
|
156 | ### Tree#clone()
|
157 |
|
158 | Returns a new `Tree` object that is an effective clone of the original.
|
159 |
|
160 | ### Tree#prune([entries])
|
161 |
|
162 | Removes any orphaned files from the graph. A file is considered orphaned if it has no path to any
|
163 | file marked as an entry.
|
164 |
|
165 | If `entries` is passed, (must be an `Array`) then any files that cannot reach _those_ files will
|
166 | be removed from the graph. (essentially overrides the internal list of entries)
|
167 |
|
168 | ### File(location, tree, [entry]) *(constructor)*
|
169 |
|
170 | Each instance represents a file in the overall build. The `location` is an absolute path, `tree`
|
171 | is the tree that contains this particular file and `entry` flags a file as an entry.
|
172 |
|
173 | Entry files are uniquely handled, particularly when it comes to `Tree#prune()`. Any files that do
|
174 | not have a path to some entry file are considered orphaned, and will be pruned.
|
175 |
|
176 | ### File#path
|
177 |
|
178 | The absolute path to where this file exists on disk.
|
179 |
|
180 | ### File#type
|
181 |
|
182 | The current file type associated with this file. This value is used to determine what plugins/hooks
|
183 | need to be invoked at various stages.
|
184 |
|
185 | **NOTE:** plugins can modify this value if their work changes the file type. (such as compiling
|
186 | `coffee` into `js`)
|
187 |
|
188 | ### File#contents
|
189 |
|
190 | This holds the current contents of the file. When first read, this property should be set, and
|
191 | subsequent changes to the source code should apply to this property.
|
192 |
|
193 | **NOTE:** must be set by a plugin.
|
194 |
|
195 | ### File#output
|
196 |
|
197 | The absolute path to where this file should be written on disk.
|
198 |
|
199 | **NOTE:** must be set by a plugin.
|
200 |
|
201 | ### File#analyzing
|
202 |
|
203 | An internal flag that helps mako know when a particular file is currently being analyzed. (to
|
204 | prevent race conditions and duplicating efforts) There is currently no public use for this
|
205 | property.
|
206 |
|
207 | ### File#analyzed
|
208 |
|
209 | A flag that helps mako know when a particular file has already been analyzed, so it doesn't
|
210 | continuously analyze the same file during subsequent builds. Do not change this manually,
|
211 | instead use `File#dirty()`.
|
212 |
|
213 | ### File#isEntry()
|
214 |
|
215 | Short-hand for `tree.isEntry(file.path)`.
|
216 |
|
217 | ### File#hasDependency(child)
|
218 |
|
219 | Short-hand for `tree.hasDependency(file.path, child)`.
|
220 |
|
221 | ### File#addDependency(child)
|
222 |
|
223 | Short-hand for `tree.addDependency(file.path, child)`.
|
224 |
|
225 | ### File#removeDependency(child)
|
226 |
|
227 | Short-hand for `tree.removeDependency(file.path, child)`.
|
228 |
|
229 | ### File#dependencies([options])
|
230 |
|
231 | Short-hand for `tree.dependenciesOf(file.path, options)`.
|
232 |
|
233 | ### File#hasDependant(parent)
|
234 |
|
235 | Short-hand for `tree.hasDependant(file.path, parent)`.
|
236 |
|
237 | ### File#addDependant(parent)
|
238 |
|
239 | Short-hand for `tree.addDependency(file.path, parent)`.
|
240 |
|
241 | ### File#removeDependant(parent)
|
242 |
|
243 | Short-hand for `tree.removeDependant(file.path, parent)`.
|
244 |
|
245 | ### File#dependants([options])
|
246 |
|
247 | Short-hand for `tree.dependantsOf(file.path, options)`.
|
248 |
|
249 | ### File#dirty()
|
250 |
|
251 | Can be used by the `prewrite` hook to mark a file as "dirty" so that it should be analyzed again.
|
252 |
|
253 | For example, [mako-stat](http://github.com/makojs/stat) will use this method whenever the
|
254 | modification time for a file has changed, which indicates to mako that analyze needs to be run
|
255 | again for this file.
|
256 |
|
257 | ### File#time(label)
|
258 |
|
259 | Start a timer using the given `label` to describe what is being timed. (eg: "read", "babel")
|
260 | For simple hooks/plugins, a single timer is all you need.
|
261 |
|
262 | If a plugin wants to have multiple timers, it should use it's name as a prefix. (eg: "js:pack",
|
263 | "css:dependencies")
|
264 |
|
265 | ### File#timeEnd(label)
|
266 |
|
267 | Stops the timer using the given `label`.
|
268 |
|
269 | The value saved here will be aggregated by the tree to give a glimpse of the overall time spent
|
270 | by varying plugins.
|
271 |
|
272 | ### File#clone(tree)
|
273 |
|
274 | Returns a new `File` object that is an effective clone of the original.
|