UNPKG

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