UNPKG

20.1 kBMarkdownView Raw
1# Substrat - Relax your build ![Build Status](https://api.travis-ci.org/BonsaiDen/substrat.png)
2
3**Substrat** is a powerful yet simple build system for HTML5 projects.
4
5It's easy and quick to set up, configurable, extendable and built with for
6frontend heavy, single page applications.
7
8![npm Details](https://nodei.co/npm/substrat.png)
9
10
11## Features
12
13- Automatic monitoring and syncing our source and build directories
14- Supports complex [file patterns](#patterns) for file filtering and ordering
15- Has task dependencies to re-build files when other files are changed (based on patterns)
16- Built in static web server with support for automatic page reload on each build
17- Comes with many built-in tasks for things like:
18
19 - JS Minification (using [UglifyJS](https://github.com/mishoo/UglifyJS2))
20 - Stylesheet compilation (using [lesscss](https://github.com/less/less.js))
21 - HTML Templating (using [Jade](https://github.com/visionmedia/jade))
22 - Markdown to HTML (using [markdown-js](https://github.com/evilstreak/markdown-js))
23 - Generating files from templates (using [mustache.js](https://github.com/janl/mustache.js))
24 - Dynamic file generation (using your custom functions)
25
26- Easily set up proxies:
27
28 - Avoid CORS configuration and other issues during local development
29 - Proxy a directory and easily inject mocks for your tests
30 - Add delays to all requests in order to simulate bad networks
31
32- Is easy to extend with your own, custom tasks
33- Completely generic, can be used with (e.g. [Grunt](http://gruntjs.com), [Jake](https://github.com/mde/jake) or any other task runner or in a standlone script)
34
35
36## Usage
37
381. Get it via `npm install substrat`
39
40 ```javascript
41 var substrat = require('substrat');
42 ```
43
442. Setup your patterns, these allow you to group and filter the files for your build
45
46 ```javascript
47 var patterns = {
48
49 js: {
50 // Match all source files of the application, and put app and config
51 // at the end when generating a array of the filenames from this pattern
52 app: substrat.pattern(/js\/.*\.js$/).last('js/config.js', 'js/app.js'),
53
54 // Match all the javascript source files of the libraries, but ignore any pre-minified ones
55 lib: substrat.pattern(/lib\/.*\.js$/).not(/\.min\.js$/)
56 },
57
58 compile: {
59 jade: substrat.pattern(/\.jade$/),
60 less: substrat.pattern(/\.less$/)
61 },
62
63 // Match all style sheets both generated and existing ones
64 // but put the generated ones at the end when generating a array of the
65 // filenames from this pattern
66 style: substrat.pattern(/(\.css|\.less)$/).last(/\.less$/),
67
68 // A matcher for everything else
69 all: substrat.pattern('*')
70
71 };
72 ```
73
743. Define an environment for the use in your templates, you can also expose the patterns so you can include all your scripts and styles automatically
75
76 ```javascript
77 var env = {
78 title: 'Substrat',
79 version: 0.1,
80 patterns: patterns // Expose the patterns for later usage
81 };
82 ```
83
844. Create a new instance of substrat with your specific configuration
85
86 ```javascript
87 var s = substrat.init({
88
89 // The source directory to watch
90 src: 'src',
91
92 // The destination directory for the build
93 dest: 'public',
94
95 // Whether or not to log build events, will still print general info
96 silent: false,
97
98 // Will disable all logging (sets silent to true)
99 quiet: false,
100
101 // If true, will produce lots of internal logging output
102 debug: false,
103
104 // Enable compression in tasks (e.g. strip whitespace, minify js etc.)
105 compress: false,
106
107 // Set up dependencies
108 depends: [
109 // Rebuild src/index.jade every time a js or less file changes
110 // This way, the template can automatically update the included
111 // scripts and styles
112 ['index.jade', [patterns.js, patterns.style]]
113 ],
114
115 // Define the tasks
116 // Tasks are run in order, each task will filter out the files it matched
117 // so they are not subject to any further tasks in the chain
118 tasks: [
119
120 // Compile all app specific scripts with uglify-js
121 substrat.task.compile(patterns.js.app, 'js'),
122
123 // Compile all jade files to html and supply them with the locals from "env"
124 substrat.task.compile(patterns.compile.jade, 'jade', env),
125
126 // Compile all less stylesheets to css
127 substrat.task.compile(patterns.compile.less, 'less'),
128
129 // Copy all other files which did not match any previous tasks
130 substrat.task.copy(patterns.all)
131
132 ],
133
134 // Setup some proxies for testing and local database access
135 proxy: {
136
137 // Proxy the local couchdb instance to avoid messy CORS setup during
138 // development
139 '/couchdb': {
140
141 // URL is the target of the proxy
142 host: 'localhost',
143 port: 5984,
144
145 // Add 500 milliseconds of delay to each request
146 delay: 500
147
148 },
149
150 // Proxy the "public" directory itself but "mock" out a couple of
151 // files to inject test mocks / frameworks
152 '/test': {
153
154 // The directory to serve
155 root: 'public',
156
157 // Replace the main application file and include additional files
158 // for testing
159 mock: {
160 'js/app.js': [
161 'test/e2e/app.js',
162 'test/e2e/mocks.js'
163 ]
164 }
165
166 }
167
168 }
169
170 });
171 ```
172
1735. Start your continous build that automatically reloads your browser while you're editing
174
175 ```javascript
176 s.listen(4444);
177 ```
178
179Read on for more details on the configuration options and tasks.
180
181
182## Configuration Options
183
184- `src`: *String*
185
186 The source directory which contains the file to build.
187
188- `dest`: *String*
189
190 The destination directory were the files produced by the build are to be found.
191
192 The contents of the directory are automatically synced with the source,
193 meaning that files and folders which no longer exist in the source directory
194 will automatically be removed.
195
196- `silent`: *Boolean (false)*
197
198 If `true` disables substrat logging (except for top level logs).
199
200- `quiet`: *Boolean (false)*
201
202 If `true` disables **all** substrat logging (enables `silent`).
203
204- `debug`: *Boolean (false)*
205
206 If `true` enables internal logging of substrat's components.
207
208- `hidden`: *Boolean (true)*
209
210 When `true` substrat will ingore any dotfiles.
211
212- `compress`: *Boolean(false)*
213
214 A flag which indicates to tasks that the should compress / minify their
215 output.
216
217 See the [Tasks](#tasks) section for more details.
218
219- `depends`: *Array[Array[Pattern, Pattern|Array[Patterns]]...]*
220
221 A array containing arrays of patterns which specify which files should be
222 rebuild once other files matching the specified patterns have changed.
223
224 See the [Dependencies](#dependencies) section for more details.
225
226- `tasks`: *Array*
227
228 A listing of tasks which will be executed in order once the contents of the
229 `src` directory change. Each successive tasks will filter out the files it
230 matched from the list of files that have changed.
231
232 See the [Tasks](#tasks) section for more details.
233
234- `proxy`: *Object*
235
236 A mapping of paths to proxy configurations.
237
238 See the [Proxies](#proxies) section for more details.
239
240
241## Methods
242
243- `run()` -> *this*
244
245 Invokes the build once and then finishes.
246
247 Will emit the `done` event once the build has finished.
248
249
250- `watch()` -> *this*
251
252 Will continously monitor the source directory for changed and re-build
253 automatically.
254
255 Triggers a `build` event after each completed build.
256
257
258- `listen(indexUrl, port [, host])` -> *this*
259
260 Same as `watch()` but will also start a local web server on the specified
261 `host` and `port` and will patch the specified `indexUrl` HTML file to
262 automatically reload on every build.
263
264 To disable automatic reloading, simply pass `null` as the value of `indexUrl`.
265
266
267- `stop()` -> *this*
268
269 Stops substrat in case it is watching or listening.
270
271 Triggers the `done` event.
272
273
274- `pattern(expr)` -> *Pattern*
275
276 Creates a new substrat pattern from the given expression.
277
278
279- `files(pattern)` -> *Array[String]*
280
281 Returns a list of files for the **destination** directory which match the
282 specified pattern(s).
283
284
285## Patterns
286
287Substrat makes heavy use of patterns for both file matching and listing.
288
289Patterns can be created from a variety of sources and are converted to regular
290expressions internally. You can also create pass objects of patterns which will
291be merged, as well as arrays which will concatenate their matches.
292
293> Note: All paths and files within substrat are treated relative to either the
294`src` or `dest` directories. E.g. `/home/user/project/src/js/app.js` will be
295treated as `js/app.js`.
296
297
298### From Strings
299
300All strings parsed via [minimatch](https://github.com/isaacs/minimatch) and
301converted into regular expressions. This means that you can use standard *glob*
302patterns like `**/*.js` and the like.
303
304The only exception to this rule is the special string `*` which will get
305converted to `/^.*$/`.
306
307
308### From Regular Expressions
309
310You can pass any valid regular expression as a pattern.
311
312
313### From Objects
314
315Patterns from objects are merged, they object's keys are sorted via the standard
316`sort()` function and are then used to merge the object's values into a new
317pattern.
318
319
320### From Functions
321
322Functions which are passed as pattern will get invoked with the filename they
323should test for matching. They should return either `true` or `false`.
324
325
326### From Arrays
327
328Arrays will create so called *Pattern Groups*. Pattern groups apply all included
329patterns in order and will preserve the ordering of the files returned by the
330individual sub patterns.
331
332
333### Ordering
334
335Patterns have the very useful `pattern.first(patterns...)` and
336`pattern.last(patterns...)` methods which will move the files matching the
337specified patterns either to the beginning or the end of the file list.
338
339```javascript
340substrat.pattern(/js\/.*\.js$/).first('js/config.js').last('js/init.js', 'js/afterInit.js');
341```
342
343For example, this allows you to get a list of all JavaScript files in your
344application and then put the file that defines your namespaces and configuration
345and the beginning
346of the list and the file initializing your code at the very end.
347
348### Exclusion
349
350In addition patterns can include one or more files via the
351`pattern.not(pattern...)` method.
352
353
354## Dependencies
355
356Substrat includes a minimal - but efficient - dependency management for files
357which is also based on pattern.
358
359Dependencies are specified in the format of an array with two entries. The first
360one is a pattern which describes which files will be re-build and the second
361entry being a pattern which specifies which files will trigger the re-build.
362
363```javascript
364['index.jade', [/*.js$/, /*.less$/]]
365```
366
367The above will rebuild `index.jade` every time that a `.js` or `.less` file has
368been added, changed or removed from the source directory.
369
370Of course it is also possibly to re-build multiple files, simply supply a more
371complex pattern as the first entry of the array:
372
373```javascript
374[/template\/view\/controller\/*.jade$/, [/*.js$/, /*.less$/]]
375```
376
377> Note: Every rebuild will trigger another check for dependencies.
378> This allows for the creation of dependencies that depend on other dependencies.
379
380
381## Tasks
382
383Tasks in substrat are highly configurable and easy to extend.
384
385
386### Built-in Tasks
387
388- __Compile__
389
390 `substrat.task.compile(pattern, compiler[, config])`
391
392 Compiles all the files matching the `pattern` from the `src` to the `dest`
393 using the specified `compiler`. Following compilers are available out of the box:
394
395 > Note: The compile tasks by will **only** obfuscate and/or minify their
396 > output when the `substrat.compress` option is set.
397
398 - `js`
399
400 Compiles JavaScript files using `uglify-js`, if the `substrat.compress`
401 option is **enabled**, otherwise it will simply copy the JS files.
402
403 __Example: Minifying all applications JS files__
404
405 substrat.task.compile('js/**/*.js', 'js')
406
407 - `less`
408
409 Compiles `less` files into CSS, changing the file extension in the process.
410 If `substrat.compress` is set it will stip whitespace from the output files.
411
412 __Example: Transforming less files into CSS__
413
414 substrat.task.compile(/*\.less$/, 'less')
415
416 - `jade`
417
418 Compiles `jade` files into HTML, changing the file extension in the process.
419 The `config` paramter should be an object and will be populate the **locals**
420 of the template.
421
422 __Example: Converting all jade templates into HTML__
423
424 substrat.task.compile(/*\.jade$/, 'jade', config)
425
426
427- __Concat__
428
429 `substrat.task.compile(pattern, type, outputFile)`
430
431 This task is pretty much the same as `compile` task but only supports `js`
432 and `less` at the moment and will merge all the files into the specified
433 `outputFile`.
434
435
436- __Copy__
437
438 `substrat.task.compile(pattern)`
439
440 Copies all the files matching the `pattern` from the `src` to the `dest`
441 directory. This task uses `fs.stream` interally for efficient copying and
442 will create directories in the destination as requried.
443
444 __Example: Copying all outstanding files as the last task__
445
446 substrat.task.copy('*')
447
448
449- __Template__
450
451 `substrat.task.template(pattern, locals [, tags])`
452
453 Compiles all files matching the `pattern` as `mustache.js` templates and
454 supplies them with `locals`. The files get rendered to a file with the same
455 name in the `dest` directory.
456
457 The optional`tags` array can be used to replace the default tags used in
458 mustache templates with custom ones. e.g. `['<%', '%>']`.
459
460 __Example: Rendering configuration file with custom tags to keep it JSHint friendly__
461
462 substrat.task.template('js/config.js', config, ['"{{', '}}"']),
463
464
465### Custom Tasks
466
467New tasks can be created via the `substrat.Task` constructor:
468
469 new substrat.Task(taskName, filePattern, handler, config)
470
471- `taskName`: *String*
472
473 This simply is a internal name for the task which is used in debug logging.
474
475- `filePattern`: *Pattern*
476
477 A substrat pattern which describes all files for which the task should be
478 executed.
479
480 > Note: A `null` pattern will run the task on every build, not matter which
481 > files have changed.
482
483- `handlerDescription`: *Object*
484
485 A object which implements the actual logic of the task.
486
487- `config`: *Object*
488
489 Additional configuration which is available to the task logic during
490 execution.
491
492
493### Task Handler Description
494
495A task handler description consists of a number of properties and methods:
496
497 var handlerDescription = {
498
499 // Run the task independently for each file
500 mode: substrat.Task.Each,
501
502 // Automatically provide the file data to the task
503 data: true,
504
505 // Map the source files to html files in the output
506 map: function(e, file) {
507 return file.replace(/\.jade$/, '.html');
508 },
509
510 // The actual task logic
511 run: function(e, done) {
512
513 try {
514
515 // Use the custom configuration of the task as the locals
516 var locals = util.merge(e.config, {
517 pretty: !e.options.compress,
518 substrat: e.substrat
519 });
520
521 done(null, jade.render(e.data.toString(), locals));
522
523 } catch(err) {
524 done(err);
525 }
526
527 }
528
529 };
530
531
532- `mode`: *Integer*
533
534 One of the following:
535
536 - `Each`
537
538 Run the task independently for each file, meaning that for five input
539 files the task will be run five times.
540
541 - `All`
542
543 Run the task once on all files, meaning that for five input files
544 the task will be called exactly one time and will be provided with all
545 the files and their data at once.
546
547 - `Single`
548
549 Run the task once and don't care about the input. Useful for auto
550 generation of files and other things.
551
552
553- `data`: *Boolean|Function(e)*
554
555 Whether or not to automatically read the input file(s) and supply their
556 buffers to the task. Can also be a function which gets passed the [task
557 execution environment](#task-execution-environment) and should return a
558 `boolean`.
559
560
561- `map`: *Function(e, file)*
562
563 A function which maps the input filename to the respective outputs, can
564 also return an array with multiple output names (e.g. a JS file and its
565 corresponding source map file).
566
567 It's arguments consists of the
568 [task execution environment](#task-execution-environment) and the path of
569 the file in source directory.
570
571 These mappings are used to create the output files of the task in the
572 destination directory.
573
574 In addition, they also server to synchronize the destination directory and
575 automatically remove files which are no longer exist in the source.
576
577 They are also available via `substrat.files(patterns)` and can be used to
578 automatically include files in HTML and other templates.
579
580
581- `run`: *Function(e, done(err[, data]))*
582
583 A function which performs the actual task logic.
584
585 It's arguments consists of the
586 [task execution environment](#task-execution-environment) and a callback
587 function.
588
589 The `done` callback takes the following arguments:
590
591 - `err`: *Null|Error*
592
593 The error value in case the task could failed. Pass `null` if the task
594 was successful.A
595
596 - `data`: *String|Array[String]* (Optional)
597
598 The file data to be written into the files indicated by the return
599 value(s) of the handlers `map()` function.
600
601 If left out, no file be written. This can be used by tasks which handle
602 the writing on their own (e.g. the `copy` task which uses streams).
603
604
605### Task Execution Environment
606
607This "environment" argument is passed to all functions of a task handler
608description and has the following structure:
609
610- `options`: *Object*
611
612 A reference to the configuration object passed into `substrat.init()`.
613
614- `config`: *Object*
615
616 A reference to the configuration object passed into the task constructor.
617
618- `mapped`: *String|Array[String]*
619
620 The filename(s) returned by the `map()` function of the task handler.
621
622 *Only for tasks running with mode `Task.Each` or `Task.All`*
623
624- `source`: *String*
625
626 The filename from the source directory.
627
628 *Only for tasks running with mode `Task.Each`*
629
630- `data`: *Buffer*
631
632 A `Buffer` object with the contents of the file reference by `source`.
633
634 *Only for tasks running with mode `Task.Each`*
635
636- `path`: *String*
637
638 The full path to the file in the source directory.
639
640 *Only for tasks running with mode `Task.Each`*
641
642- `all`: *Array[Object]*
643
644 A array of objects with `source`, `data` and `path` properties as described
645 above.
646
647 *Only for tasks running with mode `Task.All`*
648
649
650## Proxies
651
652Substrat can be used to quickly configure proxies to both http endpoints as well
653as local directories, this is done via the `substrat.proxy` option which takes a
654mapping of absolute paths to **proxy configuration objects** having the following
655structure:
656
657- `host`: *String*
658
659- `port`: *Integer*
660
661- `delay`: *Integer*
662
663- `root`: *String*
664
665- `mock`: *Object*
666
667
668## Outstanding Features / Fixes
669
670- Add a grunt task
671- Create a repository with a demo/example project
672- Add Support for a `Subfile.js`
673- Correctly write out source maps for JS and CSS files
674
675
676## License
677
678**Substrat** is licenses under MIT.
679