UNPKG

19.8 kBMarkdownView Raw
1# base-app [![NPM version](https://img.shields.io/npm/v/base-app.svg?style=flat)](https://www.npmjs.com/package/base-app) [![NPM monthly downloads](https://img.shields.io/npm/dm/base-app.svg?style=flat)](https://npmjs.org/package/base-app) [![NPM total downloads](https://img.shields.io/npm/dt/base-app.svg?style=flat)](https://npmjs.org/package/base-app) [![Linux Build Status](https://img.shields.io/travis/node-base/base-app.svg?style=flat&label=Travis)](https://travis-ci.org/node-base/base-app)
2
3> Starting point for creating a base application, with a few light plugins for running tasks and writing to the file system, and a functional CLI.
4
5Please consider following this project's author, [Jon Schlinkert](https://github.com/jonschlinkert), and consider starring the project to show your :heart: and support.
6
7- [Install](#install)
8- [Quickstart](#quickstart)
9- [CLI](#cli)
10- [API Documentation](#api-documentation)
11- [.cwd](#cwd)
12- [File System API](#file-system-api)
13 * [.src](#src)
14 * [.symlink](#symlink)
15 * [.dest](#dest)
16 * [.copy](#copy)
17- [Task API](#task-api)
18 * [.task](#task)
19 * [.build](#build)
20 * [.series](#series)
21 * [.parallel](#parallel)
22- [Events](#events)
23 * [starting](#starting)
24 * [finished](#finished)
25 * [error](#error)
26 * [task:starting](#taskstarting)
27 * [task:finished](#taskfinished)
28 * [task:error](#taskerror)
29 * [.dataLoader](#dataloader)
30- [Plugin API](#plugin-api)
31 * [.use](#use)
32 * [.run](#run)
33- [Options API](#options-api)
34 * [.option](#option)
35 * [.hasOption](#hasoption)
36 * [.enable](#enable)
37 * [.disable](#disable)
38 * [.enabled](#enabled)
39 * [.disabled](#disabled)
40 * [.isTrue](#istrue)
41 * [.isFalse](#isfalse)
42 * [.isBoolean](#isboolean)
43 * [.option.set](#optionset)
44 * [.option.get](#optionget)
45 * [.option.create](#optioncreate)
46- [Data API](#data-api)
47 * [.data](#data)
48 * [.data.extend](#dataextend)
49 * [.data.merge](#datamerge)
50 * [.data.union](#dataunion)
51 * [.data.set](#dataset)
52 * [.data.get](#dataget)
53 * [Glob patterns](#glob-patterns)
54 * [Namespacing](#namespacing)
55- [About](#about)
56
57_(TOC generated by [verb](https://github.com/verbose/verb) using [markdown-toc](https://github.com/jonschlinkert/markdown-toc))_
58
59## Install
60
61Install with [npm](https://www.npmjs.com/):
62
63```sh
64$ npm install --save base-app
65```
66
67## Quickstart
68
69Below we provide a more detailed explanation of how to get started. But if you're familiar with node.js and prefer a fast-track:
70
71**Install**
72
73```sh
74$ npm i -g base-app
75```
76
77**Create an "app"**
78
79Then create a `basefile.js` with the following code:
80
81```js
82module.exports = function(app, base) {
83 app.task('default', function(cb) {
84 console.log('task >', this.name);
85 cb();
86 });
87};
88```
89
90**Run base**
91
92In the command line, run:
93
94```sh
95$ base
96```
97
98If everthing installed correctly, you should see `task > default` in the command line.
99
100## CLI
101
102**Installing the CLI**
103
104To run base from the command line, you'll need to install `base-app` globally first. You can that now with the following command:
105
106```sh
107$ npm i -g base-app
108```
109
110This adds the `base` command to your system path, allowing it to be run from any directory or sub-directory in a project.
111
112**How the CLI works**
113
114When the `base` command is run, the globally installed `base-app` looks for a locally installed [base](https://github.com/node-base/base) module using node's `require()` system.
115
116If a locally installed [base](https://github.com/node-base/base) is found, the CLI loads the local installation of the [base](https://github.com/node-base/base) library. If a local [base](https://github.com/node-base/base) module is not found, the globally installed `base-app` will be used.
117
118Once the module is resolved, base applies the configuration from your `basefile.js` then executes any [generators](https://github.com/node-base/base-generators) or tasks you've specified for base to run.
119
120## .cwd
121
122Getter/setter that ensures the current working directory is always a fully resolved absolute filepath.
123
124```js
125app.cwd = 'foo';
126console.log(app.cwd);
127//=> /User/dev/base-app/foo
128```
129
130## File System API
131
132### .src
133
134Glob patterns or filepaths to source files.
135
136**Params**
137
138* `glob` **{String|Array}**: Glob patterns or file paths to source files.
139* `options` **{Object}**: Options or locals to merge into the context and/or pass to `src` plugins
140
141**Example**
142
143```js
144app.src('src/*.hbs', {layout: 'default'});
145```
146
147### .symlink
148
149Glob patterns or paths for symlinks.
150
151**Params**
152
153* `glob` **{String|Array}**
154
155**Example**
156
157```js
158app.symlink('src/**');
159```
160
161### .dest
162
163Specify a destination for processed files.
164
165**Params**
166
167* `dest` **{String|Function}**: File path or rename function.
168* `options` **{Object}**: Options and locals to pass to `dest` plugins
169
170**Example**
171
172```js
173app.dest('dist/');
174```
175
176### .copy
177
178Copy files with the given glob `patterns` to the specified `dest`.
179
180**Params**
181
182* `patterns` **{String|Array}**: Glob patterns of files to copy.
183* `dest` **{String|Function}**: Desination directory.
184* `returns` **{Stream}**: Stream, to continue processing if necessary.
185
186**Example**
187
188```js
189app.task('assets', function(cb) {
190 app.copy('assets/**', 'dist/')
191 .on('error', cb)
192 .on('finish', cb)
193});
194```
195
196## Task API
197
198Methods for running tasks are from the [base-task](https://github.com/base/base-task) plugin, which uses [composer](https://github.com/doowb/composer). Additional documentation can be found on those libaries.
199
200### .task
201
202Register a task
203
204**Params**
205
206* `name` **{String}**: Task name to register (tasks are cached on `app.tasks`)
207* `dependencies` **{String|Array|Function}**: String, list or array of tasks.
208* `callback` **{Function}**: Function to be called when the task is executed. Task functions should either return a stream or call the callback to let [composer](https://github.com/doowb/composer) know when the task is finished.
209
210**Examples**
211
212Register a task.
213
214```js
215app.task('default', function() {
216 // return the stream to signal "done"
217 return app.src('pages/*.hbs')
218 .pipe(app.dest('dist'));
219});
220```
221
222Register a task with dependencies (other tasks to run before executing the task):
223
224```js
225app.task('site', ['styles'], function() {
226 return app.src('pages/*.hbs')
227 .pipe(app.dest('dist'));
228});
229
230app.task('default', ['site']);
231```
232
233**Get a task**
234
235```js
236var task = app.task('site');
237```
238
239### .build
240
241Run a task or array of tasks.
242
243**Example**
244
245```js
246app.build('default', function(err, results) {
247 if (err) {
248 console.error(err);
249 return;
250 }
251 console.log(results);
252});
253```
254
255### .series
256
257Compose task or list of tasks into a single function that runs the tasks in series.
258
259**Params**
260
261* `tasks` **{String|Array|Function}**: List of tasks by name, function, or array of names/functions.
262* `returns` **{Function}**: Composed function that may take a callback function.
263
264**Example**
265
266```js
267app.task('foo', function(cb) {
268 console.log('this is foo');
269 cb();
270});
271
272var fn = app.series('foo', function(cb) {
273 console.log('this is bar');
274 cb();
275});
276
277fn(function(err) {
278 if (err) return console.error(err);
279 console.log('finished');
280});
281//=> this is foo
282//=> this is bar
283//=> finished
284```
285
286### .parallel
287
288Compose task or list of tasks into a single function that runs the tasks in parallel.
289
290**Params**
291
292* `tasks` **{String|Array|Function}**: List of tasks by name, function, or array of names/functions.
293* `returns` **{Function}**: Composed function that may take a callback function.
294
295**Example**
296
297```js
298app.task('foo', function(cb) {
299 setTimeout(function() {
300 console.log('this is foo');
301 cb();
302 }, 500);
303});
304
305var fn = app.parallel('foo', function(cb) {
306 console.log('this is bar');
307 cb();
308});
309
310fn(function(err) {
311 if (err) return console.error(err);
312 console.log('finished');
313});
314//=> this is bar
315//=> this is foo
316//=> finished
317```
318
319## Events
320
321The following events are emitted by [composer](https://github.com/doowb/composer). See the composer docs for more details
322
323### starting
324
325Emitted when a `build` is starting.
326
327```js
328app.on('starting', function(app, build) {});
329```
330
331The event emits 2 arguments:
332
3331. the current instance of [composer](https://github.com/doowb/composer) as the `app` and
3342. An object with `build` runtime information:
335
336* `.date`: an object with the `.start` time as a `Date` object.
337* `.hr`: an object with the `.start` time as an `hrtime` array.
338
339### finished
340
341Emitted when a `build` is finished.
342
343```js
344app.on('finished', function(app, build) {});
345```
346
347The event emits 2 arguments:
348
3491. `app`: instance of [composer](https://github.com/doowb/composer)
3502. `build`: an object with build runtime information:
351
352* `.date`: object with `.start` and `.end` properties, with staring and ending times of the build as `Date` objects.
353* `.hr`: object with `.start`, `.end`, `.duration`, and `.diff` properties with timing information calculated using `process.hrtime`
354
355### error
356
357Emitted when an error occurrs during a `build`.
358
359```js
360app.on('error', function(err) {});
361```
362
363### task:starting
364
365Emitted when a task is starting.
366
367```js
368app.on('task:starting', function(task, run) {});
369```
370
371### task:finished
372
373Emitted when a task has finished.
374
375```js
376app.on('task:finished', function(task, run) {});
377```
378
379### task:error
380
381Emitted when an error occurrs while running a task.
382
383```js
384app.on('task:error', function(err) {});
385```
386
387### .dataLoader
388
389Register a data loader for loading data onto `app.cache.data`.
390
391**Params**
392
393* `ext` **{String}**: The file extension for to match to the loader
394* `fn` **{Function}**: The loader function.
395
396**Example**
397
398```js
399var yaml = require('js-yaml');
400
401app.dataLoader('yml', function(str, fp) {
402 return yaml.safeLoad(str);
403});
404
405app.data('foo.yml');
406//=> loads and parses `foo.yml` as yaml
407```
408
409## Plugin API
410
411### .use
412
413Define a plugin function to be called immediately upon init. The only parameter exposed to the plugin is the application instance.
414
415Also, if a plugin returns a function, the function will be pushed
416onto the `fns` array, allowing the plugin to be called at a
417later point, elsewhere in the application.
418
419**Params**
420
421* `fn` **{Function}**: plugin function to call
422* `returns` **{Object}**: Returns the item instance for chaining.
423
424**Example**
425
426```js
427// define a plugin
428function foo(app) {
429 // do stuff
430}
431
432// register plugins
433var app = new Base()
434 .use(foo)
435 .use(bar)
436 .use(baz)
437```
438
439### .run
440
441Run all plugins
442
443**Params**
444
445* `value` **{Object}**: Object to be modified by plugins.
446* `returns` **{Object}**: Returns the item instance for chaining.
447
448**Example**
449
450```js
451var config = {};
452app.run(config);
453```
454
455## Options API
456
457### .option
458
459Set or get an option.
460
461**Params**
462
463* `key` **{String}**: The option name.
464* `value` **{any}**: The value to set.
465* `returns` **{any}**: Returns a `value` when only `key` is defined.
466
467**Example**
468
469```js
470app.option('a', true);
471app.option('a');
472//=> true
473```
474
475### .hasOption
476
477Return true if `options.hasOwnProperty(key)`
478
479**Params**
480
481* `prop` **{String}**
482* `returns` **{Boolean}**: True if `prop` exists.
483
484**Example**
485
486```js
487app.hasOption('a');
488//=> false
489app.option('a', 'b');
490app.hasOption('a');
491//=> true
492```
493
494### .enable
495
496Enable `key`.
497
498**Params**
499
500* `key` **{String}**
501* `returns` **{Object}** `Options`: to enable chaining
502
503**Example**
504
505```js
506app.enable('a');
507```
508
509### .disable
510
511Disable `key`.
512
513**Params**
514
515* `key` **{String}**: The option to disable.
516* `returns` **{Object}** `Options`: to enable chaining
517
518**Example**
519
520```js
521app.disable('a');
522```
523
524### .enabled
525
526Check if `prop` is enabled (truthy).
527
528**Params**
529
530* `prop` **{String}**
531* `returns` **{Boolean}**
532
533**Example**
534
535```js
536app.enabled('a');
537//=> false
538
539app.enable('a');
540app.enabled('a');
541//=> true
542```
543
544### .disabled
545
546Check if `prop` is disabled (falsey).
547
548**Params**
549
550* `prop` **{String}**
551* `returns` **{Boolean}**: Returns true if `prop` is disabled.
552
553**Example**
554
555```js
556app.disabled('a');
557//=> true
558
559app.enable('a');
560app.disabled('a');
561//=> false
562```
563
564### .isTrue
565
566Returns true if the value of `prop` is strictly `true`.
567
568**Params**
569
570* `prop` **{String}**
571* `returns` **{Boolean}**: Uses strict equality for comparison.
572
573**Example**
574
575```js
576app.option('a', 'b');
577app.isTrue('a');
578//=> false
579
580app.option('c', true);
581app.isTrue('c');
582//=> true
583
584app.option({a: {b: {c: true}}});
585app.isTrue('a.b.c');
586//=> true
587```
588
589### .isFalse
590
591Returns true if the value of `key` is strictly `false`.
592
593**Params**
594
595* `prop` **{String}**
596* `returns` **{Boolean}**: Uses strict equality for comparison.
597
598**Example**
599
600```js
601app.option('a', null);
602app.isFalse('a');
603//=> false
604
605app.option('c', false);
606app.isFalse('c');
607//=> true
608
609app.option({a: {b: {c: false}}});
610app.isFalse('a.b.c');
611//=> true
612```
613
614### .isBoolean
615
616Return true if the value of key is either `true` or `false`.
617
618**Params**
619
620* `key` **{String}**
621* `returns` **{Boolean}**: True if `true` or `false`.
622
623**Example**
624
625```js
626app.option('a', 'b');
627app.isBoolean('a');
628//=> false
629
630app.option('c', true);
631app.isBoolean('c');
632//=> true
633```
634
635### .option.set
636
637Set option `key` on `app.options` with the given `value`
638
639**Params**
640
641* `key` **{String}**: Option key, dot-notation may be used.
642* `value` **{any}**
643
644**Example**
645
646```js
647app.option.set('a', 'b');
648console.log(app.option.get('a'));
649//=> 'b'
650```
651
652### .option.get
653
654Get option `key` from `app.options`
655
656**Params**
657
658* `key` **{String}**: Option key, dot-notation may be used.
659* `returns` **{any}**
660
661**Example**
662
663```js
664app.option({a: 'b'});
665console.log(app.option.get('a'));
666//=> 'b'
667```
668
669### .option.create
670
671Returns a shallow clone of `app.options` with all of the options methods, as well as a `.merge` method for merging options onto the cloned object.
672
673**Params**
674
675* `options` **{Options}**: Object to merge onto the returned options object.
676* `returns` **{Object}**
677
678**Example**
679
680```js
681var opts = app.option.create();
682opts.merge({foo: 'bar'});
683```
684
685## Data API
686
687### .data
688
689Load data onto `app.cache.data`
690
691**Params**
692
693* `key` **{String|Object}**: Key of the value to set, or object to extend.
694* `val` **{any}**
695* `returns` **{Object}**: Returns the instance of `Template` for chaining
696
697**Example**
698
699```js
700console.log(app.cache.data);
701//=> {};
702
703app.data('a', 'b');
704app.data({c: 'd'});
705console.log(app.cache.data);
706//=> {a: 'b', c: 'd'}
707
708// set an array
709app.data('e', ['f']);
710
711// overwrite the array
712app.data('e', ['g']);
713
714// update the array
715app.data('e', ['h'], true);
716console.log(app.cache.data.e);
717//=> ['g', 'h']
718```
719
720### .data.extend
721
722Shallow extend an object onto `app.cache.data`.
723
724**Params**
725
726* `key` **{String|Object}**: Property name or object to extend onto `app.cache.data`. Dot-notation may be used for extending nested properties.
727* `value` **{Object}**: The object to extend onto `app.cache.data`
728* `returns` **{Object}**: returns the instance for chaining
729
730**Example**
731
732```js
733app.data({a: {b: {c: 'd'}}});
734app.data.extend('a.b', {x: 'y'});
735console.log(app.get('a.b'));
736//=> {c: 'd', x: 'y'}
737```
738
739### .data.merge
740
741Deeply merge an object onto `app.cache.data`.
742
743**Params**
744
745* `key` **{String|Object}**: Property name or object to merge onto `app.cache.data`. Dot-notation may be used for merging nested properties.
746* `value` **{Object}**: The object to merge onto `app.cache.data`
747* `returns` **{Object}**: returns the instance for chaining
748
749**Example**
750
751```js
752app.data({a: {b: {c: {d: {e: 'f'}}}}});
753app.data.merge('a.b', {c: {d: {g: 'h'}}});
754console.log(app.get('a.b'));
755//=> {c: {d: {e: 'f', g: 'h'}}}
756```
757
758### .data.union
759
760Union the given value onto a new or existing array value on `app.cache.data`.
761
762**Params**
763
764* `key` **{String}**: Property name. Dot-notation may be used for nested properties.
765* `array` **{Object}**: The array to add or union on `app.cache.data`
766* `returns` **{Object}**: returns the instance for chaining
767
768**Example**
769
770```js
771app.data({a: {b: ['c', 'd']}});
772app.data.union('a.b', ['e', 'f']}});
773console.log(app.get('a.b'));
774//=> ['c', 'd', 'e', 'f']
775```
776
777### .data.set
778
779Set the given value onto `app.cache.data`.
780
781**Params**
782
783* `key` **{String|Object}**: Property name or object to merge onto `app.cache.data`. Dot-notation may be used for nested properties.
784* `val` **{any}**: The value to set on `app.cache.data`
785* `returns` **{Object}**: returns the instance for chaining
786
787**Example**
788
789```js
790app.data.set('a.b', ['c', 'd']}});
791console.log(app.get('a'));
792//=> {b: ['c', 'd']}
793```
794
795### .data.get
796
797Get the value of `key` from `app.cache.data`. Dot-notation may be used for getting nested properties.
798
799**Params**
800
801* `key` **{String}**: The name of the property to get.
802* `returns` **{any}**: Returns the value of `key`
803
804**Example**
805
806```js
807app.data({a: {b: {c: 'd'}}});
808console.log(app.get('a.b'));
809//=> {c: 'd'}
810```
811
812### Glob patterns
813
814Glob patterns may be passed as a string or array. All of these work:
815
816```js
817app.data('foo.json');
818app.data('*.json');
819app.data(['*.json']);
820// pass options to node-glob
821app.data(['*.json'], {dot: true});
822```
823
824### Namespacing
825
826Namespacing allows you to load data onto a specific key, optionally using part of the file path as the key.
827
828**Example**
829
830Given that `foo.json` contains `{a: 'b'}`:
831
832```js
833app.data('foo.json');
834console.log(app.cache.data);
835//=> {a: 'b'}
836
837app.data('foo.json', {namespace: true});
838console.log(app.cache.data);
839//=> {foo: {a: 'b'}}
840
841app.data('foo.json', {
842 namespace: function(fp) {
843 return path.basename(fp);
844 }
845});
846console.log(app.cache.data);
847//=> {'foo.json': {a: 'b'}}
848```
849
850## About
851
852<details>
853<summary><strong>Contributing</strong></summary>
854
855Pull requests and stars are always welcome. For bugs and feature requests, [please create an issue](../../issues/new).
856
857</details>
858
859<details>
860<summary><strong>Running Tests</strong></summary>
861
862Running and reviewing unit tests is a great way to get familiarized with a library and its API. You can install dependencies and run tests with the following command:
863
864```sh
865$ npm install && npm test
866```
867
868</details>
869
870<details>
871<summary><strong>Building docs</strong></summary>
872
873_(This project's readme.md is generated by [verb](https://github.com/verbose/verb-generate-readme), please don't edit the readme directly. Any changes to the readme must be made in the [.verb.md](.verb.md) readme template.)_
874
875To generate the readme, run the following command:
876
877```sh
878$ npm install -g verbose/verb#dev verb-generate-readme && verb
879```
880
881</details>
882
883### Related projects
884
885You might also be interested in these projects:
886
887* [base-fs](https://www.npmjs.com/package/base-fs): base-methods plugin that adds vinyl-fs methods to your 'base' application for working with the file… [more](https://github.com/node-base/base-fs) | [homepage](https://github.com/node-base/base-fs "base-methods plugin that adds vinyl-fs methods to your 'base' application for working with the file system, like src, dest, copy and symlink.")
888* [base-pipeline](https://www.npmjs.com/package/base-pipeline): base-methods plugin that adds pipeline and plugin methods for dynamically composing streaming plugin pipelines. | [homepage](https://github.com/node-base/base-pipeline "base-methods plugin that adds pipeline and plugin methods for dynamically composing streaming plugin pipelines.")
889* [base](https://www.npmjs.com/package/base): Framework for rapidly creating high quality, server-side node.js applications, using plugins like building blocks | [homepage](https://github.com/node-base/base "Framework for rapidly creating high quality, server-side node.js applications, using plugins like building blocks")
890
891### Author
892
893**Jon Schlinkert**
894
895* [LinkedIn Profile](https://linkedin.com/in/jonschlinkert)
896* [GitHub Profile](https://github.com/jonschlinkert)
897* [Twitter Profile](https://twitter.com/jonschlinkert)
898
899### License
900
901Copyright © 2018, [Jon Schlinkert](https://github.com/jonschlinkert).
902Released under the [MIT License](LICENSE).
903
904***
905
906_This file was generated by [verb-generate-readme](https://github.com/verbose/verb-generate-readme), v0.6.0, on April 13, 2018._
\No newline at end of file